mirror of
https://github.com/tailwindlabs/tailwindcss.git
synced 2025-12-08 21:36:08 +00:00
simplify animation tests and improve stability (#4250)
This will make sure that we tackle a few additional edge cases: - When the `name` is the same as a reserved keyword, then it will be used as a `name` as well. E.g.: 1s ease ease infinite; Will result in a name of `ease` as well. - We take care of trimming and multiple spaces. - We don't generate 8k tests anymore, which means that these specific tests only take a second instead of 10 seconds.
This commit is contained in:
parent
0c5c5409d7
commit
c7bcfe52cd
@ -6,26 +6,46 @@ const TIMINGS = new Set(['linear', 'ease', 'ease-in', 'ease-out', 'ease-in-out']
|
||||
const TIMING_FNS = ['cubic-bezier', 'steps']
|
||||
|
||||
const COMMA = /\,(?![^(]*\))/g // Comma separator that is not located between brackets. E.g.: `cubiz-bezier(a, b, c)` these don't count.
|
||||
const SPACE = /\ (?![^(]*\))/g // Similar to the one above, but with spaces instead.
|
||||
const SPACE = /\ +(?![^(]*\))/g // Similar to the one above, but with spaces instead.
|
||||
const TIME = /^(-?[\d.]+m?s)$/
|
||||
const DIGIT = /^(\d+)$/
|
||||
|
||||
export default function parseAnimationValue(input) {
|
||||
const animations = input.split(COMMA)
|
||||
const result = animations.map((animation) => {
|
||||
const result = {}
|
||||
const parts = animation.split(SPACE)
|
||||
let animations = input.split(COMMA)
|
||||
let result = animations.map((animation) => {
|
||||
let result = {}
|
||||
let parts = animation.trim().split(SPACE)
|
||||
let seen = new Set()
|
||||
|
||||
for (let part of parts) {
|
||||
if (DIRECTIONS.has(part)) result.direction = part
|
||||
else if (PLAY_STATES.has(part)) result.playState = part
|
||||
else if (FILL_MODES.has(part)) result.fillMode = part
|
||||
else if (ITERATION_COUNTS.has(part)) result.iterationCount = part
|
||||
else if (TIMINGS.has(part)) result.timingFunction = part
|
||||
else if (TIMING_FNS.some((f) => part.startsWith(`${f}(`))) result.timingFunction = part
|
||||
else if (TIME.test(part)) result[result.duration === undefined ? 'duration' : 'delay'] = part
|
||||
else if (DIGIT.test(part)) result.iterationCount = part
|
||||
else result.name = part
|
||||
if (!seen.has('DIRECTIONS') && DIRECTIONS.has(part)) {
|
||||
result.direction = part
|
||||
seen.add('DIRECTIONS')
|
||||
} else if (!seen.has('PLAY_STATES') && PLAY_STATES.has(part)) {
|
||||
result.playState = part
|
||||
seen.add('PLAY_STATES')
|
||||
} else if (!seen.has('FILL_MODES') && FILL_MODES.has(part)) {
|
||||
result.fillMode = part
|
||||
seen.add('FILL_MODES')
|
||||
} else if (
|
||||
!seen.has('ITERATION_COUNTS') &&
|
||||
(ITERATION_COUNTS.has(part) || DIGIT.test(part))
|
||||
) {
|
||||
result.iterationCount = part
|
||||
seen.add('ITERATION_COUNTS')
|
||||
} else if (!seen.has('TIMING_FUNCTION') && TIMINGS.has(part)) {
|
||||
result.timingFunction = part
|
||||
seen.add('TIMING_FUNCTION')
|
||||
} else if (!seen.has('TIMING_FUNCTION') && TIMING_FNS.some((f) => part.startsWith(`${f}(`))) {
|
||||
result.timingFunction = part
|
||||
seen.add('TIMING_FUNCTION')
|
||||
} else if (!seen.has('DURATION') && TIME.test(part)) {
|
||||
result.duration = part
|
||||
seen.add('DURATION')
|
||||
} else if (!seen.has('DELAY') && TIME.test(part)) {
|
||||
result.delay = part
|
||||
seen.add('DELAY')
|
||||
} else result.name = part
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
@ -1,11 +1,15 @@
|
||||
import parseAnimationValue from '../src/util/parseAnimationValue'
|
||||
import { produce } from './util/produce'
|
||||
|
||||
describe('Tailwind Defaults', () => {
|
||||
it.each([
|
||||
[
|
||||
'spin 1s linear infinite',
|
||||
{ name: 'spin', duration: '1s', timingFunction: 'linear', iterationCount: 'infinite' },
|
||||
{
|
||||
name: 'spin',
|
||||
duration: '1s',
|
||||
timingFunction: 'linear',
|
||||
iterationCount: 'infinite',
|
||||
},
|
||||
],
|
||||
[
|
||||
'ping 1s cubic-bezier(0, 0, 0.2, 1) infinite',
|
||||
@ -16,15 +20,6 @@ describe('Tailwind Defaults', () => {
|
||||
iterationCount: 'infinite',
|
||||
},
|
||||
],
|
||||
[
|
||||
'pulse 2s cubic-bezier(0.4, 0, 0.6) infinite',
|
||||
{
|
||||
name: 'pulse',
|
||||
duration: '2s',
|
||||
timingFunction: 'cubic-bezier(0.4, 0, 0.6)',
|
||||
iterationCount: 'infinite',
|
||||
},
|
||||
],
|
||||
['bounce 1s infinite', { name: 'bounce', duration: '1s', iterationCount: 'infinite' }],
|
||||
])('should be possible to parse: "%s"', (input, expected) => {
|
||||
expect(parseAnimationValue(input)).toEqual(expected)
|
||||
@ -48,7 +43,12 @@ describe('MDN Examples', () => {
|
||||
],
|
||||
[
|
||||
'slidein 3s linear 1s',
|
||||
{ delay: '1s', duration: '3s', name: 'slidein', timingFunction: 'linear' },
|
||||
{
|
||||
delay: '1s',
|
||||
duration: '3s',
|
||||
name: 'slidein',
|
||||
timingFunction: 'linear',
|
||||
},
|
||||
],
|
||||
['slidein 3s', { duration: '3s', name: 'slidein' }],
|
||||
])('should be possible to parse: "%s"', (input, expected) => {
|
||||
@ -59,44 +59,28 @@ describe('MDN Examples', () => {
|
||||
describe('duration & delay', () => {
|
||||
it.each([
|
||||
// Positive seconds (integer)
|
||||
['spin 1s 1s linear', { duration: '1s', delay: '1s' }],
|
||||
['spin 2s 1s linear', { duration: '2s', delay: '1s' }],
|
||||
['spin 1s 2s linear', { duration: '1s', delay: '2s' }],
|
||||
|
||||
// Negative seconds (integer)
|
||||
['spin -1s -1s linear', { duration: '-1s', delay: '-1s' }],
|
||||
['spin -2s -1s linear', { duration: '-2s', delay: '-1s' }],
|
||||
['spin -1s -2s linear', { duration: '-1s', delay: '-2s' }],
|
||||
|
||||
// Positive seconds (float)
|
||||
['spin 1.321s 1.321s linear', { duration: '1.321s', delay: '1.321s' }],
|
||||
['spin 2.321s 1.321s linear', { duration: '2.321s', delay: '1.321s' }],
|
||||
['spin 1.321s 2.321s linear', { duration: '1.321s', delay: '2.321s' }],
|
||||
|
||||
// Negative seconds (float)
|
||||
['spin -1.321s -1.321s linear', { duration: '-1.321s', delay: '-1.321s' }],
|
||||
['spin -2.321s -1.321s linear', { duration: '-2.321s', delay: '-1.321s' }],
|
||||
['spin -1.321s -2.321s linear', { duration: '-1.321s', delay: '-2.321s' }],
|
||||
|
||||
// Positive milliseconds (integer)
|
||||
['spin 100ms 100ms linear', { duration: '100ms', delay: '100ms' }],
|
||||
['spin 200ms 100ms linear', { duration: '200ms', delay: '100ms' }],
|
||||
['spin 100ms 200ms linear', { duration: '100ms', delay: '200ms' }],
|
||||
|
||||
// Negative milliseconds (integer)
|
||||
['spin -100ms -100ms linear', { duration: '-100ms', delay: '-100ms' }],
|
||||
['spin -200ms -100ms linear', { duration: '-200ms', delay: '-100ms' }],
|
||||
['spin -100ms -200ms linear', { duration: '-100ms', delay: '-200ms' }],
|
||||
|
||||
// Positive milliseconds (float)
|
||||
['spin 100.321ms 100.321ms linear', { duration: '100.321ms', delay: '100.321ms' }],
|
||||
['spin 200.321ms 100.321ms linear', { duration: '200.321ms', delay: '100.321ms' }],
|
||||
['spin 100.321ms 200.321ms linear', { duration: '100.321ms', delay: '200.321ms' }],
|
||||
|
||||
// Negative milliseconds (float)
|
||||
['spin -100.321ms -100.321ms linear', { duration: '-100.321ms', delay: '-100.321ms' }],
|
||||
['spin -200.321ms -100.321ms linear', { duration: '-200.321ms', delay: '-100.321ms' }],
|
||||
['spin -100.321ms -200.321ms linear', { duration: '-100.321ms', delay: '-200.321ms' }],
|
||||
])('should be possible to parse "%s" into %o', (input, { duration, delay }) => {
|
||||
const parsed = parseAnimationValue(input)
|
||||
expect(parsed.duration).toEqual(duration)
|
||||
@ -127,34 +111,6 @@ describe('iteration count', () => {
|
||||
)
|
||||
})
|
||||
|
||||
describe('iteration count', () => {
|
||||
it.each([
|
||||
// Number
|
||||
['1 spin 200s 100s linear', '1'],
|
||||
['spin 2 200s 100s linear', '2'],
|
||||
['spin 200s 3 100s linear', '3'],
|
||||
['spin 200s 100s 4 linear', '4'],
|
||||
['spin 200s 100s linear 5', '5'],
|
||||
['100 spin 200s 100s linear', '100'],
|
||||
['spin 200 200s 100s linear', '200'],
|
||||
['spin 200s 300 100s linear', '300'],
|
||||
['spin 200s 100s 400 linear', '400'],
|
||||
['spin 200s 100s linear 500', '500'],
|
||||
|
||||
// Infinite
|
||||
['infinite spin 200s 100s linear', 'infinite'],
|
||||
['spin infinite 200s 100s linear', 'infinite'],
|
||||
['spin 200s infinite 100s linear', 'infinite'],
|
||||
['spin 200s 100s infinite linear', 'infinite'],
|
||||
['spin 200s 100s linear infinite', 'infinite'],
|
||||
])(
|
||||
'should be possible to parse "%s" with an iteraction count of "%s"',
|
||||
(input, iterationCount) => {
|
||||
expect(parseAnimationValue(input).iterationCount).toEqual(iterationCount)
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
describe('multiple animations', () => {
|
||||
it('should be possible to parse multiple applications at once', () => {
|
||||
const input = [
|
||||
@ -166,7 +122,12 @@ describe('multiple animations', () => {
|
||||
const parsed = parseAnimationValue(input)
|
||||
expect(parsed).toHaveLength(3)
|
||||
expect(parsed).toEqual([
|
||||
{ name: 'spin', duration: '1s', timingFunction: 'linear', iterationCount: 'infinite' },
|
||||
{
|
||||
name: 'spin',
|
||||
duration: '1s',
|
||||
timingFunction: 'linear',
|
||||
iterationCount: 'infinite',
|
||||
},
|
||||
{
|
||||
name: 'ping',
|
||||
duration: '1s',
|
||||
@ -183,46 +144,21 @@ describe('multiple animations', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('randomized crazy big examples', () => {
|
||||
function reOrder(input, offset = 0) {
|
||||
return [...input.slice(offset), ...input.slice(0, offset)]
|
||||
}
|
||||
|
||||
it.each(
|
||||
produce((choose) => {
|
||||
const direction = choose('normal', 'reverse', 'alternate', 'alternate-reverse')
|
||||
const playState = choose('running', 'paused')
|
||||
const fillMode = choose('none', 'forwards', 'backwards', 'both')
|
||||
const iterationCount = choose('infinite', '1', '100')
|
||||
const timingFunction = choose(
|
||||
'linear',
|
||||
'ease',
|
||||
'ease-in',
|
||||
'ease-out',
|
||||
'ease-in-out',
|
||||
'cubic-bezier(0, 0, 0.2, 1)',
|
||||
'steps(4, end)'
|
||||
)
|
||||
const name = choose('animation-name-a', 'animation-name-b')
|
||||
const inputArgs = [direction, playState, fillMode, iterationCount, timingFunction, name]
|
||||
const orderOffset = choose(...Array(inputArgs.length).keys())
|
||||
|
||||
return [
|
||||
// Input
|
||||
reOrder(inputArgs, orderOffset).join(' '),
|
||||
|
||||
// Output
|
||||
{
|
||||
direction,
|
||||
playState,
|
||||
fillMode,
|
||||
iterationCount,
|
||||
timingFunction,
|
||||
name,
|
||||
},
|
||||
]
|
||||
})
|
||||
)('should be possible to parse "%s"', (input, output) => {
|
||||
expect(parseAnimationValue(input)).toEqual(output)
|
||||
})
|
||||
it.each`
|
||||
input | direction | playState | fillMode | iterationCount | timingFunction | duration | delay | name
|
||||
${'1s spin 1s infinite'} | ${undefined} | ${undefined} | ${undefined} | ${'infinite'} | ${undefined} | ${'1s'} | ${'1s'} | ${'spin'}
|
||||
${'infinite infinite 1s 1s'} | ${undefined} | ${undefined} | ${undefined} | ${'infinite'} | ${undefined} | ${'1s'} | ${'1s'} | ${'infinite'}
|
||||
${'ease 1s ease 1s'} | ${undefined} | ${undefined} | ${undefined} | ${undefined} | ${'ease'} | ${'1s'} | ${'1s'} | ${'ease'}
|
||||
${'normal paused backwards infinite ease-in 1s 2s name'} | ${'normal'} | ${'paused'} | ${'backwards'} | ${'infinite'} | ${'ease-in'} | ${'1s'} | ${'2s'} | ${'name'}
|
||||
${'paused backwards infinite ease-in 1s 2s name normal'} | ${'normal'} | ${'paused'} | ${'backwards'} | ${'infinite'} | ${'ease-in'} | ${'1s'} | ${'2s'} | ${'name'}
|
||||
${'backwards infinite ease-in 1s 2s name normal paused'} | ${'normal'} | ${'paused'} | ${'backwards'} | ${'infinite'} | ${'ease-in'} | ${'1s'} | ${'2s'} | ${'name'}
|
||||
${'infinite ease-in 1s 2s name normal paused backwards'} | ${'normal'} | ${'paused'} | ${'backwards'} | ${'infinite'} | ${'ease-in'} | ${'1s'} | ${'2s'} | ${'name'}
|
||||
${'ease-in 1s 2s name normal paused backwards infinite'} | ${'normal'} | ${'paused'} | ${'backwards'} | ${'infinite'} | ${'ease-in'} | ${'1s'} | ${'2s'} | ${'name'}
|
||||
${'1s 2s name normal paused backwards infinite ease-in'} | ${'normal'} | ${'paused'} | ${'backwards'} | ${'infinite'} | ${'ease-in'} | ${'1s'} | ${'2s'} | ${'name'}
|
||||
${'2s name normal paused backwards infinite ease-in 1s'} | ${'normal'} | ${'paused'} | ${'backwards'} | ${'infinite'} | ${'ease-in'} | ${'2s'} | ${'1s'} | ${'name'}
|
||||
${'name normal paused backwards infinite ease-in 1s 2s'} | ${'normal'} | ${'paused'} | ${'backwards'} | ${'infinite'} | ${'ease-in'} | ${'1s'} | ${'2s'} | ${'name'}
|
||||
${' name normal paused backwards infinite ease-in 1s 2s '} | ${'normal'} | ${'paused'} | ${'backwards'} | ${'infinite'} | ${'ease-in'} | ${'1s'} | ${'2s'} | ${'name'}
|
||||
`('should parse "$input" correctly', ({ input, ...expected }) => {
|
||||
let parsed = parseAnimationValue(input)
|
||||
expect(parsed).toEqual(expected)
|
||||
})
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user