mirror of
https://github.com/tailwindlabs/tailwindcss.git
synced 2025-12-08 21:36:08 +00:00
* Run test suite against both engines * make eslint happy * only run `stable` tests on Node 12 * use normal expectation instead of snapshot file When we run the tests only against `stable` (for node 12), then the snapshots exists for the `Oxide` build. They are marked as `obsolete` and will cause the `npm run test` script to fail. Sadly. Inlined them for now, but ideally we make those tests more blackbox-y so that we test that we get source maps and that we can map the sourcemap back to the input files (without looking at the actual annotations). * properly indent inline css Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com> Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
360 lines
14 KiB
JavaScript
360 lines
14 KiB
JavaScript
import { finalizeSelector } from '../src/util/formatVariantSelector'
|
|
import { crosscheck } from './util/run'
|
|
|
|
crosscheck(() => {
|
|
it('should be possible to add a simple variant to a simple selector', () => {
|
|
let selector = '.text-center'
|
|
let candidate = 'hover:text-center'
|
|
|
|
let formats = [{ format: '&:hover', isArbitraryVariant: false }]
|
|
|
|
expect(finalizeSelector(selector, formats, { candidate })).toEqual('.hover\\:text-center:hover')
|
|
})
|
|
|
|
it('should be possible to add a multiple simple variants to a simple selector', () => {
|
|
let selector = '.text-center'
|
|
let candidate = 'focus:hover:text-center'
|
|
|
|
let formats = [
|
|
{ format: '&:hover', isArbitraryVariant: false },
|
|
{ format: '&:focus', isArbitraryVariant: false },
|
|
]
|
|
|
|
expect(finalizeSelector(selector, formats, { candidate })).toEqual(
|
|
'.focus\\:hover\\:text-center:hover:focus'
|
|
)
|
|
})
|
|
|
|
it('should be possible to add a simple variant to a selector containing escaped parts', () => {
|
|
let selector = '.bg-\\[rgba\\(0\\,0\\,0\\)\\]'
|
|
let candidate = 'hover:bg-[rgba(0,0,0)]'
|
|
|
|
let formats = [{ format: '&:hover', isArbitraryVariant: false }]
|
|
|
|
expect(finalizeSelector(selector, formats, { candidate })).toEqual(
|
|
'.hover\\:bg-\\[rgba\\(0\\2c 0\\2c 0\\)\\]:hover'
|
|
)
|
|
})
|
|
|
|
it('should be possible to add a simple variant to a selector containing escaped parts (escape is slightly different)', () => {
|
|
let selector = '.bg-\\[rgba\\(0\\2c 0\\2c 0\\)\\]'
|
|
let candidate = 'hover:bg-[rgba(0,0,0)]'
|
|
|
|
let formats = [{ format: '&:hover', isArbitraryVariant: false }]
|
|
|
|
expect(finalizeSelector(selector, formats, { candidate })).toEqual(
|
|
'.hover\\:bg-\\[rgba\\(0\\2c 0\\2c 0\\)\\]:hover'
|
|
)
|
|
})
|
|
|
|
it('should be possible to add a simple variant to a more complex selector', () => {
|
|
let selector = '.space-x-4 > :not([hidden]) ~ :not([hidden])'
|
|
let candidate = 'hover:space-x-4'
|
|
|
|
let formats = [{ format: '&:hover', isArbitraryVariant: false }]
|
|
|
|
expect(finalizeSelector(selector, formats, { candidate })).toEqual(
|
|
'.hover\\:space-x-4:hover > :not([hidden]) ~ :not([hidden])'
|
|
)
|
|
})
|
|
|
|
it('should be possible to add multiple simple variants to a more complex selector', () => {
|
|
let selector = '.space-x-4 > :not([hidden]) ~ :not([hidden])'
|
|
let candidate = 'disabled:focus:hover:space-x-4'
|
|
|
|
let formats = [
|
|
{ format: '&:hover', isArbitraryVariant: false },
|
|
{ format: '&:focus', isArbitraryVariant: false },
|
|
{ format: '&:disabled', isArbitraryVariant: false },
|
|
]
|
|
|
|
expect(finalizeSelector(selector, formats, { candidate })).toEqual(
|
|
'.disabled\\:focus\\:hover\\:space-x-4:hover:focus:disabled > :not([hidden]) ~ :not([hidden])'
|
|
)
|
|
})
|
|
|
|
it('should be possible to add a single merge variant to a simple selector', () => {
|
|
let selector = '.text-center'
|
|
let candidate = 'group-hover:text-center'
|
|
|
|
let formats = [{ format: ':merge(.group):hover &', isArbitraryVariant: false }]
|
|
|
|
expect(finalizeSelector(selector, formats, { candidate })).toEqual(
|
|
'.group:hover .group-hover\\:text-center'
|
|
)
|
|
})
|
|
|
|
it('should be possible to add multiple merge variants to a simple selector', () => {
|
|
let selector = '.text-center'
|
|
let candidate = 'group-focus:group-hover:text-center'
|
|
|
|
let formats = [
|
|
{ format: ':merge(.group):hover &', isArbitraryVariant: false },
|
|
{ format: ':merge(.group):focus &', isArbitraryVariant: false },
|
|
]
|
|
|
|
expect(finalizeSelector(selector, formats, { candidate })).toEqual(
|
|
'.group:focus:hover .group-focus\\:group-hover\\:text-center'
|
|
)
|
|
})
|
|
|
|
it('should be possible to add a single merge variant to a more complex selector', () => {
|
|
let selector = '.space-x-4 ~ :not([hidden]) ~ :not([hidden])'
|
|
let candidate = 'group-hover:space-x-4'
|
|
|
|
let formats = [{ format: ':merge(.group):hover &', isArbitraryVariant: false }]
|
|
|
|
expect(finalizeSelector(selector, formats, { candidate })).toEqual(
|
|
'.group:hover .group-hover\\:space-x-4 ~ :not([hidden]) ~ :not([hidden])'
|
|
)
|
|
})
|
|
|
|
it('should be possible to add multiple merge variants to a more complex selector', () => {
|
|
let selector = '.space-x-4 ~ :not([hidden]) ~ :not([hidden])'
|
|
let candidate = 'group-focus:group-hover:space-x-4'
|
|
|
|
let formats = [
|
|
{ format: ':merge(.group):hover &', isArbitraryVariant: false },
|
|
{ format: ':merge(.group):focus &', isArbitraryVariant: false },
|
|
]
|
|
|
|
expect(finalizeSelector(selector, formats, { candidate })).toEqual(
|
|
'.group:focus:hover .group-focus\\:group-hover\\:space-x-4 ~ :not([hidden]) ~ :not([hidden])'
|
|
)
|
|
})
|
|
|
|
it('should be possible to add multiple unique merge variants to a simple selector', () => {
|
|
let selector = '.text-center'
|
|
let candidate = 'peer-focus:group-hover:text-center'
|
|
|
|
let formats = [
|
|
{ format: ':merge(.group):hover &', isArbitraryVariant: false },
|
|
{ format: ':merge(.peer):focus ~ &' },
|
|
]
|
|
|
|
expect(finalizeSelector(selector, formats, { candidate })).toEqual(
|
|
'.peer:focus ~ .group:hover .peer-focus\\:group-hover\\:text-center'
|
|
)
|
|
})
|
|
|
|
it('should be possible to add multiple unique merge variants to a simple selector', () => {
|
|
let selector = '.text-center'
|
|
let candidate = 'group-hover:peer-focus:text-center'
|
|
|
|
let formats = [
|
|
{ format: ':merge(.peer):focus ~ &', isArbitraryVariant: false },
|
|
{ format: ':merge(.group):hover &', isArbitraryVariant: false },
|
|
]
|
|
|
|
expect(finalizeSelector(selector, formats, { candidate })).toEqual(
|
|
'.group:hover .peer:focus ~ .group-hover\\:peer-focus\\:text-center'
|
|
)
|
|
})
|
|
|
|
it('should be possible to use multiple :merge() calls with different "arguments"', () => {
|
|
let selector = '.foo'
|
|
let candidate = 'peer-focus:group-focus:peer-hover:group-hover:foo'
|
|
|
|
let formats = [
|
|
{ format: ':merge(.group):hover &', isArbitraryVariant: false },
|
|
{ format: ':merge(.peer):hover ~ &', isArbitraryVariant: false },
|
|
{ format: ':merge(.group):focus &', isArbitraryVariant: false },
|
|
{ format: ':merge(.peer):focus ~ &', isArbitraryVariant: false },
|
|
]
|
|
|
|
expect(finalizeSelector(selector, formats, { candidate })).toEqual(
|
|
'.peer:focus:hover ~ .group:focus:hover .peer-focus\\:group-focus\\:peer-hover\\:group-hover\\:foo'
|
|
)
|
|
})
|
|
|
|
it('group hover and prose headings combination', () => {
|
|
let selector = '.text-center'
|
|
let candidate = 'group-hover:prose-headings:text-center'
|
|
let formats = [
|
|
{ format: ':where(&) :is(h1, h2, h3, h4)', isArbitraryVariant: false }, // Prose Headings
|
|
{ format: ':merge(.group):hover &', isArbitraryVariant: false }, // Group Hover
|
|
]
|
|
|
|
expect(finalizeSelector(selector, formats, { candidate })).toEqual(
|
|
'.group:hover :where(.group-hover\\:prose-headings\\:text-center) :is(h1, h2, h3, h4)'
|
|
)
|
|
})
|
|
|
|
it('group hover and prose headings combination flipped', () => {
|
|
let selector = '.text-center'
|
|
let candidate = 'prose-headings:group-hover:text-center'
|
|
let formats = [
|
|
{ format: ':merge(.group):hover &', isArbitraryVariant: false }, // Group Hover
|
|
{ format: ':where(&) :is(h1, h2, h3, h4)', isArbitraryVariant: false }, // Prose Headings
|
|
]
|
|
|
|
expect(finalizeSelector(selector, formats, { candidate })).toEqual(
|
|
':where(.group:hover .prose-headings\\:group-hover\\:text-center) :is(h1, h2, h3, h4)'
|
|
)
|
|
})
|
|
|
|
it('should be possible to handle a complex utility', () => {
|
|
let selector = '.space-x-4 > :not([hidden]) ~ :not([hidden])'
|
|
let candidate = 'peer-disabled:peer-first-child:group-hover:group-focus:focus:hover:space-x-4'
|
|
let formats = [
|
|
{ format: '&:hover', isArbitraryVariant: false }, // Hover
|
|
{ format: '&:focus', isArbitraryVariant: false }, // Focus
|
|
{ format: ':merge(.group):focus &', isArbitraryVariant: false }, // Group focus
|
|
{ format: ':merge(.group):hover &', isArbitraryVariant: false }, // Group hover
|
|
{ format: ':merge(.peer):first-child ~ &', isArbitraryVariant: false }, // Peer first-child
|
|
{ format: ':merge(.peer):disabled ~ &', isArbitraryVariant: false }, // Peer disabled
|
|
]
|
|
|
|
expect(finalizeSelector(selector, formats, { candidate })).toEqual(
|
|
'.peer:disabled:first-child ~ .group:hover:focus .peer-disabled\\:peer-first-child\\:group-hover\\:group-focus\\:focus\\:hover\\:space-x-4:hover:focus > :not([hidden]) ~ :not([hidden])'
|
|
)
|
|
})
|
|
|
|
it('should match base utilities that are prefixed', () => {
|
|
let context = { tailwindConfig: { prefix: 'tw-' } }
|
|
let selector = '.tw-text-center'
|
|
let candidate = 'tw-text-center'
|
|
let formats = []
|
|
|
|
expect(finalizeSelector(selector, formats, { candidate, context })).toEqual('.tw-text-center')
|
|
})
|
|
|
|
it('should prefix classes from variants', () => {
|
|
let context = { tailwindConfig: { prefix: 'tw-' } }
|
|
let selector = '.tw-text-center'
|
|
let candidate = 'foo:tw-text-center'
|
|
let formats = [{ format: '.foo &', isArbitraryVariant: false }]
|
|
|
|
expect(finalizeSelector(selector, formats, { candidate, context })).toEqual(
|
|
'.tw-foo .foo\\:tw-text-center'
|
|
)
|
|
})
|
|
|
|
it('should not prefix classes from arbitrary variants', () => {
|
|
let context = { tailwindConfig: { prefix: 'tw-' } }
|
|
let selector = '.tw-text-center'
|
|
let candidate = '[.foo_&]:tw-text-center'
|
|
let formats = [{ format: '.foo &', isArbitraryVariant: true }]
|
|
|
|
expect(finalizeSelector(selector, formats, { candidate, context })).toEqual(
|
|
'.foo .\\[\\.foo_\\&\\]\\:tw-text-center'
|
|
)
|
|
})
|
|
|
|
it('Merged selectors with mixed combinators uses the first one', () => {
|
|
// This isn't explicitly specced behavior but it is how it works today
|
|
|
|
let selector = '.text-center'
|
|
let candidate = 'text-center'
|
|
let formats = [
|
|
{ format: ':merge(.group):focus > &', isArbitraryVariant: true },
|
|
{ format: ':merge(.group):hover &', isArbitraryVariant: true },
|
|
]
|
|
|
|
expect(finalizeSelector(selector, formats, { candidate })).toEqual(
|
|
'.group:hover:focus > .text-center'
|
|
)
|
|
})
|
|
|
|
describe('real examples', () => {
|
|
it('example a', () => {
|
|
let selector = '.placeholder-red-500::placeholder'
|
|
let candidate = 'hover:placeholder-red-500'
|
|
|
|
let formats = [{ format: '&:hover', isArbitraryVariant: false }]
|
|
|
|
expect(finalizeSelector(selector, formats, { candidate })).toEqual(
|
|
'.hover\\:placeholder-red-500:hover::placeholder'
|
|
)
|
|
})
|
|
|
|
it('example b', () => {
|
|
let selector = '.space-x-4 > :not([hidden]) ~ :not([hidden])'
|
|
let candidate = 'group-hover:hover:space-x-4'
|
|
|
|
let formats = [
|
|
{ format: '&:hover', isArbitraryVariant: false },
|
|
{ format: ':merge(.group):hover &', isArbitraryVariant: false },
|
|
]
|
|
|
|
expect(finalizeSelector(selector, formats, { candidate })).toEqual(
|
|
'.group:hover .group-hover\\:hover\\:space-x-4:hover > :not([hidden]) ~ :not([hidden])'
|
|
)
|
|
})
|
|
|
|
it('should work for group-hover and class dark mode combinations', () => {
|
|
let selector = '.text-center'
|
|
let candidate = 'dark:group-hover:text-center'
|
|
|
|
let formats = [
|
|
{ format: ':merge(.group):hover &', isArbitraryVariant: false },
|
|
{ format: '.dark &', isArbitraryVariant: false },
|
|
]
|
|
|
|
expect(finalizeSelector(selector, formats, { candidate })).toEqual(
|
|
'.dark .group:hover .dark\\:group-hover\\:text-center'
|
|
)
|
|
})
|
|
|
|
it('should work for group-hover and class dark mode combinations (reversed)', () => {
|
|
let selector = '.text-center'
|
|
let candidate = 'group-hover:dark:text-center'
|
|
|
|
let formats = [
|
|
{ format: '.dark &' },
|
|
{ format: ':merge(.group):hover &', isArbitraryVariant: false },
|
|
]
|
|
|
|
expect(finalizeSelector(selector, formats, { candidate })).toEqual(
|
|
'.group:hover .dark .group-hover\\:dark\\:text-center'
|
|
)
|
|
})
|
|
|
|
describe('prose-headings', () => {
|
|
it('should be possible to use hover:prose-headings:text-center', () => {
|
|
let selector = '.text-center'
|
|
let candidate = 'hover:prose-headings:text-center'
|
|
|
|
let formats = [{ format: ':where(&) :is(h1, h2, h3, h4)' }, { format: '&:hover' }]
|
|
|
|
expect(finalizeSelector(selector, formats, { candidate })).toEqual(
|
|
':where(.hover\\:prose-headings\\:text-center) :is(h1, h2, h3, h4):hover'
|
|
)
|
|
})
|
|
|
|
it('should be possible to use prose-headings:hover:text-center', () => {
|
|
let selector = '.text-center'
|
|
let candidate = 'prose-headings:hover:text-center'
|
|
|
|
let formats = [{ format: '&:hover' }, { format: ':where(&) :is(h1, h2, h3, h4)' }]
|
|
|
|
expect(finalizeSelector(selector, formats, { candidate })).toEqual(
|
|
':where(.prose-headings\\:hover\\:text-center:hover) :is(h1, h2, h3, h4)'
|
|
)
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('pseudo elements', () => {
|
|
it.each`
|
|
before | after
|
|
${'&::before'} | ${'&::before'}
|
|
${'&::before:hover'} | ${'&:hover::before'}
|
|
${'&:before:hover'} | ${'&:hover:before'}
|
|
${'&::file-selector-button:hover'} | ${'&::file-selector-button:hover'}
|
|
${'&:hover::file-selector-button'} | ${'&:hover::file-selector-button'}
|
|
${'.parent:hover &'} | ${'.parent:hover &'}
|
|
${'.parent::before &'} | ${'.parent &::before'}
|
|
${'.parent::before &:hover'} | ${'.parent &:hover::before'}
|
|
${':where(&::before) :is(h1, h2, h3, h4)'} | ${':where(&) :is(h1, h2, h3, h4)::before'}
|
|
${':where(&::file-selector-button) :is(h1, h2, h3, h4)'} | ${':where(&::file-selector-button) :is(h1, h2, h3, h4)'}
|
|
`('should translate "$before" into "$after"', ({ before, after }) => {
|
|
let result = finalizeSelector('.a', [{ format: before, isArbitraryVariant: false }], {
|
|
candidate: 'a',
|
|
})
|
|
|
|
expect(result).toEqual(after.replace('&', '.a'))
|
|
})
|
|
})
|
|
})
|