2020-08-12 19:47:21 -04:00
import postcss from 'postcss'
import resolveConfig from '../src/util/resolveConfig'
import defaultConfig from '../stubs/defaultConfig.stub.js'
2020-08-19 09:33:03 -04:00
import tailwind from '../src/index'
2020-08-12 19:47:21 -04:00
2020-08-19 09:33:03 -04:00
function run ( input , config = { } ) {
2020-10-17 21:03:49 -04:00
return postcss ( [ tailwind ( config ) ] ) . process ( input , { from : undefined } )
2020-08-12 19:47:21 -04:00
}
test ( 'it copies class declarations into itself' , ( ) => {
const output = '.a { color: red; } .b { color: red; }'
2020-08-22 00:06:47 +02:00
expect . assertions ( 2 )
2020-10-16 15:39:44 -04:00
return run ( '.a { color: red; } .b { @apply a; }' ) . then ( ( result ) => {
2020-08-19 09:33:03 -04:00
expect ( result . css ) . toMatchCss ( output )
2020-08-12 19:47:21 -04:00
expect ( result . warnings ( ) . length ) . toBe ( 0 )
} )
} )
2020-08-22 00:03:42 +02:00
test ( 'apply values can contain tabs' , ( ) => {
const input = `
. a {
@ apply p - 4 \ tm - 4 ;
}
`
const expected = `
. a {
margin : 1 rem ;
padding : 1 rem ;
}
`
expect . assertions ( 2 )
2020-10-16 15:39:44 -04:00
return run ( input ) . then ( ( result ) => {
2020-08-22 00:03:42 +02:00
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 {
2021-04-30 16:58:40 +02:00
margin : 1 rem ;
2020-08-22 00:03:42 +02:00
display : flex ;
flex - direction : column ;
padding : 1 rem ;
}
`
expect . assertions ( 2 )
2020-10-16 15:39:44 -04:00
return run ( input ) . then ( ( result ) => {
2020-08-22 00:03:42 +02:00
expect ( result . css ) . toMatchCss ( expected )
expect ( result . warnings ( ) . length ) . toBe ( 0 )
} )
} )
2020-08-12 19:47:21 -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 ; }
`
2020-08-22 00:06:47 +02:00
expect . assertions ( 2 )
2020-10-16 15:39:44 -04:00
return run ( input ) . then ( ( result ) => {
2020-08-13 20:46:27 -04:00
expect ( result . css ) . toMatchCss ( expected )
2020-08-12 19:47:21 -04:00
expect ( result . warnings ( ) . length ) . toBe ( 0 )
} )
} )
2020-08-13 12:14:26 -04:00
test ( 'it removes important from applied classes by default' , ( ) => {
2020-08-12 19:47:21 -04:00
const input = `
. a { color : red ! important ; }
2020-08-28 08:17:18 -04:00
. a : hover { color : blue ! important ; }
2020-08-12 19:47:21 -04:00
. b { @ apply a ; }
`
const expected = `
. a { color : red ! important ; }
2020-08-28 08:17:18 -04:00
. a : hover { color : blue ! important ; }
2020-08-12 19:47:21 -04:00
. b { color : red ; }
2020-08-28 08:17:18 -04:00
. b : hover { color : blue ; }
2020-08-12 19:47:21 -04:00
`
2020-08-22 00:06:47 +02:00
expect . assertions ( 2 )
2020-10-16 15:39:44 -04:00
return run ( input ) . then ( ( result ) => {
2020-08-13 20:46:27 -04:00
expect ( result . css ) . toMatchCss ( expected )
2020-08-12 19:47:21 -04:00
expect ( result . warnings ( ) . length ) . toBe ( 0 )
} )
} )
2020-08-13 12:14:26 -04:00
test ( 'applied rules can be made !important' , ( ) => {
2020-08-12 19:47:21 -04:00
const input = `
. a { color : red ; }
. b { @ apply a ! important ; }
`
const expected = `
. a { color : red ; }
. b { color : red ! important ; }
`
2020-08-22 00:06:47 +02:00
expect . assertions ( 2 )
2020-10-16 15:39:44 -04:00
return run ( input ) . then ( ( result ) => {
2020-08-13 20:46:27 -04:00
expect ( result . css ) . toMatchCss ( expected )
2020-08-12 19:47:21 -04:00
expect ( result . warnings ( ) . length ) . toBe ( 0 )
} )
} )
2020-08-13 07:51:46 -04:00
test ( 'cssnext custom property sets are no longer supported' , ( ) => {
2020-08-12 19:47:21 -04:00
const input = `
. a {
color : red ;
}
. b {
@ apply a -- custom - property - set ;
}
`
2020-08-22 00:06:47 +02:00
expect . assertions ( 1 )
2020-10-16 15:39:44 -04:00
return run ( input ) . catch ( ( e ) => {
2020-08-13 07:51:46 -04:00
expect ( e ) . toMatchObject ( { name : 'CssSyntaxError' } )
2020-08-12 19:47:21 -04:00
} )
} )
test ( 'it fails if the class does not exist' , ( ) => {
2020-08-22 00:06:47 +02:00
expect . assertions ( 1 )
2020-10-16 15:39:44 -04:00
return run ( '.b { @apply a; }' ) . catch ( ( e ) => {
2020-08-12 19:47:21 -04:00
expect ( e ) . toMatchObject ( { name : 'CssSyntaxError' } )
} )
} )
test ( 'applying classes that are defined in a media query is supported' , ( ) => {
const input = `
@ media ( min - width : 300 px ) {
. a { color : blue ; }
}
. b {
@ apply a ;
}
`
const output = `
@ media ( min - width : 300 px ) {
. a { color : blue ; }
}
@ media ( min - width : 300 px ) {
. b { color : blue ; }
}
`
2020-08-22 00:06:47 +02:00
expect . assertions ( 2 )
2020-10-16 15:39:44 -04:00
return run ( input ) . then ( ( result ) => {
2020-08-12 19:47:21 -04:00
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 : 300 px ) {
. a { color : blue ; }
}
. b {
@ apply a ;
}
`
const output = `
. a {
color : red ;
}
@ media ( min - width : 300 px ) {
. a { color : blue ; }
}
. b {
color : red ;
}
@ media ( min - width : 300 px ) {
. b { color : blue ; }
}
`
2020-08-22 00:06:47 +02:00
expect . assertions ( 2 )
2020-10-16 15:39:44 -04:00
return run ( input ) . then ( ( result ) => {
2020-08-12 19:47:21 -04:00
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 ;
}
`
2020-08-22 00:06:47 +02:00
expect . assertions ( 2 )
2020-10-16 15:39:44 -04:00
return run ( input ) . then ( ( result ) => {
2020-08-12 19:47:21 -04:00
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 ;
}
`
2020-08-22 00:06:47 +02:00
expect . assertions ( 2 )
2020-10-16 15:39:44 -04:00
return run ( input ) . then ( ( result ) => {
2020-08-12 19:47:21 -04:00
expect ( result . css ) . toMatchCss ( output )
expect ( result . warnings ( ) . length ) . toBe ( 0 )
} )
} )
2020-08-14 19:12:45 -04:00
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 ;
}
`
2020-08-22 00:06:47 +02:00
expect . assertions ( 2 )
2020-10-16 15:39:44 -04:00
return run ( input ) . then ( ( result ) => {
2020-08-14 19:12:45 -04:00
expect ( result . css ) . toMatchCss ( output )
expect ( result . warnings ( ) . length ) . toBe ( 0 )
} )
} )
2020-08-13 12:14:26 -04:00
test ( 'you can apply utility classes that do not actually exist as long as they would exist if utilities were being generated' , ( ) => {
2020-08-12 19:47:21 -04:00
const input = `
. foo { @ apply mt - 4 ; }
`
const expected = `
. foo { margin - top : 1 rem ; }
`
2020-08-22 00:06:47 +02:00
expect . assertions ( 2 )
2020-10-16 15:39:44 -04:00
return run ( input ) . then ( ( result ) => {
2020-08-13 20:46:27 -04:00
expect ( result . css ) . toMatchCss ( expected )
2020-08-12 19:47:21 -04:00
expect ( result . warnings ( ) . length ) . toBe ( 0 )
} )
} )
2020-12-11 15:03:49 +01:00
test ( 'shadow lookup will be constructed when we have missing @tailwind atrules' , ( ) => {
2020-08-13 20:16:56 -04:00
const input = `
@ tailwind base ;
2020-12-11 15:03:49 +01:00
2020-08-13 20:16:56 -04:00
. foo { @ apply mt - 4 ; }
`
2020-08-22 00:06:47 +02:00
expect . assertions ( 1 )
2020-12-11 15:03:49 +01:00
return run ( input ) . then ( ( result ) => {
expect ( result . css ) . toContain ( ` .foo { margin-top: 1rem; \n } ` )
2020-08-13 20:16:56 -04:00
} )
} )
2020-08-13 20:46:27 -04:00
test ( 'you can apply a class that is defined in multiple rules' , ( ) => {
const input = `
. foo {
color : red ;
}
. bar {
@ apply foo ;
}
. foo {
2020-08-14 11:15:17 -04:00
opacity : . 5 ;
2020-08-13 20:46:27 -04:00
}
`
const expected = `
. foo {
color : red ;
}
. bar {
color : red ;
2020-08-14 11:15:17 -04:00
opacity : . 5 ;
2020-08-13 20:46:27 -04:00
}
. foo {
2020-08-14 11:15:17 -04:00
opacity : . 5 ;
2020-08-13 20:46:27 -04:00
}
`
2020-08-22 00:06:47 +02:00
expect . assertions ( 2 )
2020-10-16 15:39:44 -04:00
return run ( input ) . then ( ( result ) => {
2020-08-13 20:46:27 -04:00
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 : 640 px ) {
. foo {
text - align : center
}
}
`
2020-08-22 00:06:47 +02:00
expect . assertions ( 2 )
2020-10-16 15:39:44 -04:00
return run ( input ) . then ( ( result ) => {
2020-08-13 20:46:27 -04:00
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
}
`
2020-08-22 00:06:47 +02:00
expect . assertions ( 2 )
2020-10-16 15:39:44 -04:00
return run ( input ) . then ( ( result ) => {
2020-08-13 20:46:27 -04:00
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 : 640 px ) {
. foo : hover {
opacity : 0.5
}
}
`
2020-08-22 00:06:47 +02:00
expect . assertions ( 2 )
2020-10-16 15:39:44 -04:00
return run ( input ) . then ( ( result ) => {
2020-08-13 20:46:27 -04:00
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 : 640 px ) {
. foo {
max - width : 640 px ;
}
}
@ media ( min - width : 768 px ) {
. foo {
max - width : 768 px ;
}
}
@ media ( min - width : 1024 px ) {
. foo {
max - width : 1024 px ;
}
}
@ media ( min - width : 1280 px ) {
. foo {
max - width : 1280 px ;
}
}
2020-10-18 15:12:54 -04:00
@ media ( min - width : 1536 px ) {
. foo {
max - width : 1536 px ;
}
}
2020-08-13 20:46:27 -04:00
`
2020-08-22 00:06:47 +02:00
expect . assertions ( 2 )
2020-10-16 15:39:44 -04:00
return run ( input ) . then ( ( result ) => {
2020-08-13 20:46:27 -04:00
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 ;
}
`
2020-08-22 00:06:47 +02:00
expect . assertions ( 2 )
2020-10-16 15:39:44 -04:00
return run ( input ) . then ( ( result ) => {
2020-08-13 20:46:27 -04:00
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 ;
}
`
2020-08-22 00:06:47 +02:00
expect . assertions ( 2 )
2020-10-16 15:39:44 -04:00
return run ( input ) . then ( ( result ) => {
2020-08-13 20:46:27 -04:00
expect ( result . css ) . toMatchCss ( expected )
expect ( result . warnings ( ) . length ) . toBe ( 0 )
} )
} )
test ( 'you can apply classes recursively' , ( ) => {
2020-11-19 12:40:56 -05:00
const input = `
. baz {
color : blue ;
}
. bar {
@ apply baz px - 4 ;
}
. foo {
@ apply bar ;
}
`
const expected = `
. baz {
color : blue ;
}
. bar {
padding - left : 1 rem ;
padding - right : 1 rem ;
color : blue ;
}
. foo {
padding - left : 1 rem ;
padding - right : 1 rem ;
color : blue ;
}
`
expect . assertions ( 2 )
return run ( input ) . then ( ( result ) => {
expect ( result . css ) . toMatchCss ( expected )
expect ( result . warnings ( ) . length ) . toBe ( 0 )
} )
} )
2020-11-26 15:05:06 +01:00
test ( 'you can apply complex classes recursively' , ( ) => {
2020-11-19 12:40:56 -05:00
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 ;
}
`
2020-11-26 15:05:06 +01:00
2020-11-19 12:40:56 -05:00
const expected = `
2021-04-30 16:58:40 +02:00
. button {
border - radius : 0.75 rem ;
}
2020-11-19 12:40:56 -05:00
. button : focus {
-- tw - border - opacity : 1 ;
}
2020-11-26 15:05:06 +01:00
2020-11-19 12:40:56 -05:00
. button {
padding - left : 1.5 rem ;
padding - right : 1.5 rem ;
2021-04-30 16:58:40 +02:00
padding - top : 0.5 rem ;
padding - bottom : 0.5 rem ;
2020-11-19 12:40:56 -05:00
}
2020-11-26 15:05:06 +01:00
2020-11-19 12:40:56 -05:00
. button : hover {
-- tw - text - opacity : 1 ;
color : rgba ( 255 , 255 , 255 , var ( -- tw - text - opacity ) ) ;
}
2020-11-26 15:05:06 +01:00
. 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 ) ) ;
2021-04-30 16:58:40 +02:00
border - radius : 0.75 rem ;
2020-11-26 15:05:06 +01:00
}
2020-11-19 12:40:56 -05:00
. button - yellow : focus {
-- tw - border - opacity : 1 ;
}
2020-11-26 15:05:06 +01:00
2020-11-19 12:40:56 -05:00
. button - yellow {
padding - left : 1.5 rem ;
padding - right : 1.5 rem ;
2021-04-30 16:58:40 +02:00
padding - top : 0.5 rem ;
padding - bottom : 0.5 rem ;
2020-11-19 12:40:56 -05:00
}
2020-11-26 15:05:06 +01:00
2020-11-19 12:40:56 -05:00
. 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' , ( ) => {
2020-08-13 20:46:27 -04:00
const input = `
. foo {
@ apply bar ;
}
. bar {
@ apply baz ;
}
. baz {
color : blue ;
}
`
const expected = `
. foo {
color : blue ;
}
. bar {
color : blue ;
}
. baz {
color : blue ;
}
`
2020-08-22 00:06:47 +02:00
expect . assertions ( 2 )
2020-10-16 15:39:44 -04:00
return run ( input ) . then ( ( result ) => {
2020-08-13 20:46:27 -04:00
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 ;
2021-04-30 16:58:40 +02:00
text - align : right ;
2020-08-13 20:46:27 -04:00
opacity : 0.5 ;
}
. foo : hover {
opacity : 1 ;
}
@ media ( min - width : 640 px ) {
. foo {
vertical - align : middle ;
}
}
. foo {
color : red ;
}
`
2020-08-22 00:06:47 +02:00
expect . assertions ( 2 )
2020-10-16 15:39:44 -04:00
return run ( input ) . then ( ( result ) => {
2020-08-13 20:46:27 -04:00
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 = `
2021-04-30 16:58:40 +02:00
. foo {
position : absolute ;
}
2020-08-13 20:46:27 -04:00
. foo : hover {
2020-11-16 17:45:55 +01:00
-- tw - bg - opacity : 1 ;
background - color : rgba ( 255 , 255 , 255 , var ( -- tw - bg - opacity ) ) ;
2020-08-13 20:46:27 -04:00
}
. foo {
text - align : right ;
}
2021-04-30 16:58:40 +02:00
. foo : hover {
opacity : 0.5 ;
}
2020-08-13 20:46:27 -04:00
@ media ( min - width : 640 px ) {
. foo {
text - align : center ;
vertical - align : middle ;
}
}
`
2020-08-22 00:06:47 +02:00
expect . assertions ( 2 )
2020-10-16 15:39:44 -04:00
return run ( input ) . then ( ( result ) => {
2020-08-13 20:46:27 -04:00
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 ;
}
}
}
`
2020-08-22 00:06:47 +02:00
expect . assertions ( 2 )
2020-10-16 15:39:44 -04:00
return run ( input ) . then ( ( result ) => {
2020-08-13 20:46:27 -04:00
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 ;
2021-04-30 16:58:40 +02:00
text - align : right ;
2020-08-13 20:46:27 -04:00
}
. baz . bar : hover {
opacity : 0.5 ;
}
@ media ( min - width : 768 px ) {
. baz . bar {
float : right ;
}
}
}
`
2020-08-22 00:06:47 +02:00
expect . assertions ( 2 )
2020-10-16 15:39:44 -04:00
return run ( input ) . then ( ( result ) => {
2020-08-13 20:46:27 -04:00
expect ( result . css ) . toMatchCss ( expected )
expect ( result . warnings ( ) . length ) . toBe ( 0 )
} )
} )
2020-08-14 11:15:45 -04:00
describe ( 'using apply with the prefix option' , ( ) => {
test ( 'applying a class including the prefix' , ( ) => {
const input = `
. foo { @ apply tw - mt - 4 ; }
`
2020-08-12 19:47:21 -04:00
2020-08-14 11:15:45 -04:00
const expected = `
. foo { margin - top : 1 rem ; }
`
2020-08-12 19:47:21 -04:00
2020-08-14 11:15:45 -04:00
const config = resolveConfig ( [
{
... defaultConfig ,
prefix : 'tw-' ,
} ,
] )
2020-08-12 19:47:21 -04:00
2020-08-22 00:06:47 +02:00
expect . assertions ( 2 )
2020-10-16 15:39:44 -04:00
return run ( input , config ) . then ( ( result ) => {
2020-08-14 11:15:45 -04:00
expect ( result . css ) . toMatchCss ( expected )
expect ( result . warnings ( ) . length ) . toBe ( 0 )
} )
2020-08-12 19:47:21 -04:00
} )
2020-08-14 11:15:45 -04:00
test ( 'applying a class including the prefix when using a prefix function' , ( ) => {
const input = `
. foo { @ apply tw - func - mt - 4 ; }
`
2020-08-12 19:47:21 -04:00
2020-08-14 11:15:45 -04:00
const expected = `
. foo { margin - top : 1 rem ; }
`
2020-08-12 19:47:21 -04:00
2020-08-14 11:15:45 -04:00
const config = resolveConfig ( [
{
... defaultConfig ,
prefix : ( ) => {
return 'tw-func-'
} ,
2020-08-12 19:47:21 -04:00
} ,
2020-08-14 11:15:45 -04:00
] )
2020-08-12 19:47:21 -04:00
2020-08-22 00:06:47 +02:00
expect . assertions ( 2 )
2020-10-16 15:39:44 -04:00
return run ( input , config ) . then ( ( result ) => {
2020-08-14 11:15:45 -04:00
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-' ,
} ,
] )
2020-08-22 00:06:47 +02:00
expect . assertions ( 1 )
2020-10-16 15:39:44 -04:00
return run ( input , config ) . catch ( ( e ) => {
2020-08-14 11:15:45 -04:00
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-' ,
} ,
] )
2020-08-22 00:06:47 +02:00
expect . assertions ( 2 )
2020-10-16 15:39:44 -04:00
return run ( input , config ) . then ( ( result ) => {
2020-08-14 11:15:45 -04:00
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 : 1 rem ; color : red ; }
. tw - mt - 4 { color : red ; }
`
const config = resolveConfig ( [
{
... defaultConfig ,
prefix : 'tw-' ,
} ,
] )
2020-08-22 00:06:47 +02:00
expect . assertions ( 2 )
2020-10-16 15:39:44 -04:00
return run ( input , config ) . then ( ( result ) => {
2020-08-14 11:15:45 -04:00
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 )
2020-10-16 15:39:44 -04:00
return run ( input , config ) . catch ( ( e ) => {
2020-08-14 11:15:45 -04:00
expect ( e ) . toMatchObject ( {
name : 'CssSyntaxError' ,
reason : 'The `mt-4` class does not exist, but `tw-mt-4` does. Did you forget the prefix?' ,
} )
} )
2020-08-12 19:47:21 -04:00
} )
2020-08-14 12:23:28 -04:00
2020-10-19 09:35:22 -04:00
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 = `
2020-11-26 14:53:58 +01:00
. foo { @ apply anteater ; }
`
2020-10-19 09:35:22 -04:00
2020-11-26 14:53:58 +01:00
const config = resolveConfig ( [ { ... defaultConfig } ] )
2020-10-19 09:35:22 -04:00
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." ,
} )
} )
} )
2020-08-14 12:23:28 -04:00
test ( 'you can apply classes with important and a prefix enabled' , ( ) => {
const input = `
. foo { @ apply tw - mt - 4 ; }
`
const expected = `
. foo { margin - top : 1 rem ; }
`
const config = resolveConfig ( [
{
... defaultConfig ,
prefix : 'tw-' ,
important : true ,
} ,
] )
2020-08-22 00:06:47 +02:00
expect . assertions ( 2 )
2020-10-16 15:39:44 -04:00
return run ( input , config ) . then ( ( result ) => {
2020-08-14 12:23:28 -04:00
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 : 1 rem ; }
`
const config = resolveConfig ( [
{
... defaultConfig ,
prefix : 'tw-' ,
important : '#app' ,
} ,
] )
2020-08-22 00:06:47 +02:00
expect . assertions ( 2 )
2020-10-16 15:39:44 -04:00
return run ( input , config ) . then ( ( result ) => {
2020-08-14 12:23:28 -04:00
expect ( result . css ) . toMatchCss ( expected )
expect ( result . warnings ( ) . length ) . toBe ( 0 )
} )
} )
2020-08-12 19:47:21 -04:00
} )
2020-08-14 12:23:28 -04:00
test ( 'you can apply utility classes when a selector is used for the important option' , ( ) => {
2020-08-12 19:47:21 -04:00
const input = `
2020-08-13 12:14:26 -04:00
. foo {
@ apply mt - 8 mb - 8 ;
}
2020-08-12 19:47:21 -04:00
`
const expected = `
2020-08-13 12:14:26 -04:00
. foo {
margin - top : 2 rem ;
margin - bottom : 2 rem ;
}
2020-08-12 19:47:21 -04:00
`
const config = resolveConfig ( [
{
... defaultConfig ,
important : '#app' ,
} ,
] )
2020-08-22 00:06:47 +02:00
expect . assertions ( 2 )
2020-10-16 15:39:44 -04:00
return run ( input , config ) . then ( ( result ) => {
2020-08-13 20:46:27 -04:00
expect ( result . css ) . toMatchCss ( expected )
2020-08-12 19:47:21 -04:00
expect ( result . warnings ( ) . length ) . toBe ( 0 )
} )
} )
2020-08-20 16:29:07 -04:00
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 ;
}
}
2020-11-26 14:53:58 +01:00
`
2020-08-20 16:29:07 -04:00
const expected = `
@ supports ( display : grid ) {
. foo , h1 > . bar * {
float : left ;
opacity : 0.5 ;
}
. foo : hover , h1 > . bar * : hover {
opacity : 1 ;
}
@ media ( min - width : 768 px ) {
. foo , h1 > . bar * {
float : right ;
}
}
}
`
2020-10-16 15:39:44 -04:00
return run ( input ) . then ( ( result ) => {
2020-08-20 16:29:07 -04:00
expect ( result . css ) . toMatchCss ( expected )
expect ( result . warnings ( ) . length ) . toBe ( 0 )
} )
} )
2020-09-07 02:36:09 +02:00
2020-11-26 14:53:58 +01:00
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 : 768 px ) {
. 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 )
} )
} )
2020-11-26 16:12:47 +01:00
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.25 rem ;
}
. multiple h2 ,
. multiple h3 ,
. multiple h4 {
margin - top : 2 rem ;
}
`
const config = resolveConfig ( [ { ... defaultConfig , important : true } ] )
return run ( input , config ) . then ( ( result ) => {
expect ( result . css ) . toMatchCss ( expected )
expect ( result . warnings ( ) . length ) . toBe ( 0 )
} )
} )
2020-09-07 02:36:09 +02:00
test ( 'you can apply classes in a nested rule' , ( ) => {
const input = `
. selector {
& : hover {
@ apply text - white ;
}
}
`
const expected = `
. selector {
& : hover {
2020-11-16 17:45:55 +01:00
-- tw - text - opacity : 1 ;
color : rgba ( 255 , 255 , 255 , var ( -- tw - text - opacity ) ) ;
2020-09-07 02:36:09 +02:00
}
}
`
2020-10-16 15:39:44 -04:00
return run ( input ) . then ( ( result ) => {
2020-09-07 02:36:09 +02:00
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 : 200 px ) {
@ apply overflow - hidden ;
}
}
`
const expected = `
. selector {
@ media ( min - width : 200 px ) {
overflow : hidden ;
}
}
`
2020-10-16 15:39:44 -04:00
return run ( input ) . then ( ( result ) => {
2020-09-07 02:36:09 +02:00
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 : 768 px ) {
width : 33.333333 % ;
}
}
`
2020-10-16 15:39:44 -04:00
return run ( input ) . then ( ( result ) => {
2020-09-07 02:36:09 +02:00
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 : 768 px ) {
width : 33.333333 %
}
}
}
`
2020-10-16 15:39:44 -04:00
return run ( input ) . then ( ( result ) => {
2020-09-07 02:36:09 +02:00
expect ( result . css ) . toMatchCss ( expected )
expect ( result . warnings ( ) . length ) . toBe ( 0 )
} )
} )
2020-09-13 09:44:19 -04:00
test ( 'declarations within a rule that uses @apply can be !important' , ( ) => {
const input = `
. foo {
@ apply text - center ;
float : left ;
display : block ! important ;
}
`
const expected = `
2020-11-26 14:53:58 +01:00
. foo {
text - align : center ;
float : left ;
display : block ! important ;
}
2020-09-13 09:44:19 -04:00
`
expect . assertions ( 2 )
2020-10-16 15:39:44 -04:00
return run ( input ) . then ( ( result ) => {
2020-09-13 09:44:19 -04:00
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 = `
2020-11-26 14:53:58 +01:00
. foo {
text - align : center ! important ;
float : left ;
display : block ! important ;
}
2020-09-13 09:44:19 -04:00
`
expect . assertions ( 2 )
2020-10-16 15:39:44 -04:00
return run ( input ) . then ( ( result ) => {
2020-09-13 09:44:19 -04:00
expect ( result . css ) . toMatchCss ( expected )
expect ( result . warnings ( ) . length ) . toBe ( 0 )
} )
} )
2020-12-11 15:03:49 +01:00
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 : 1 rem ; }
. foo { margin - top : 1 rem ; }
` )
expect ( output2 . css ) . toMatchCss ( `
. container { max - width : 500 px ; }
. foo { margin - top : 1 rem ; }
` )
} )