Skip to content

Commit c7bcfe5

Browse files
RobinMalfaitadamwathan
authored andcommitted
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.
1 parent 0c5c540 commit c7bcfe5

File tree

2 files changed

+69
-113
lines changed

2 files changed

+69
-113
lines changed

src/util/parseAnimationValue.js

+34-14
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,46 @@ const TIMINGS = new Set(['linear', 'ease', 'ease-in', 'ease-out', 'ease-in-out']
66
const TIMING_FNS = ['cubic-bezier', 'steps']
77

88
const COMMA = /\,(?![^(]*\))/g // Comma separator that is not located between brackets. E.g.: `cubiz-bezier(a, b, c)` these don't count.
9-
const SPACE = /\ (?![^(]*\))/g // Similar to the one above, but with spaces instead.
9+
const SPACE = /\ +(?![^(]*\))/g // Similar to the one above, but with spaces instead.
1010
const TIME = /^(-?[\d.]+m?s)$/
1111
const DIGIT = /^(\d+)$/
1212

1313
export default function parseAnimationValue(input) {
14-
const animations = input.split(COMMA)
15-
const result = animations.map((animation) => {
16-
const result = {}
17-
const parts = animation.split(SPACE)
14+
let animations = input.split(COMMA)
15+
let result = animations.map((animation) => {
16+
let result = {}
17+
let parts = animation.trim().split(SPACE)
18+
let seen = new Set()
1819

1920
for (let part of parts) {
20-
if (DIRECTIONS.has(part)) result.direction = part
21-
else if (PLAY_STATES.has(part)) result.playState = part
22-
else if (FILL_MODES.has(part)) result.fillMode = part
23-
else if (ITERATION_COUNTS.has(part)) result.iterationCount = part
24-
else if (TIMINGS.has(part)) result.timingFunction = part
25-
else if (TIMING_FNS.some((f) => part.startsWith(`${f}(`))) result.timingFunction = part
26-
else if (TIME.test(part)) result[result.duration === undefined ? 'duration' : 'delay'] = part
27-
else if (DIGIT.test(part)) result.iterationCount = part
28-
else result.name = part
21+
if (!seen.has('DIRECTIONS') && DIRECTIONS.has(part)) {
22+
result.direction = part
23+
seen.add('DIRECTIONS')
24+
} else if (!seen.has('PLAY_STATES') && PLAY_STATES.has(part)) {
25+
result.playState = part
26+
seen.add('PLAY_STATES')
27+
} else if (!seen.has('FILL_MODES') && FILL_MODES.has(part)) {
28+
result.fillMode = part
29+
seen.add('FILL_MODES')
30+
} else if (
31+
!seen.has('ITERATION_COUNTS') &&
32+
(ITERATION_COUNTS.has(part) || DIGIT.test(part))
33+
) {
34+
result.iterationCount = part
35+
seen.add('ITERATION_COUNTS')
36+
} else if (!seen.has('TIMING_FUNCTION') && TIMINGS.has(part)) {
37+
result.timingFunction = part
38+
seen.add('TIMING_FUNCTION')
39+
} else if (!seen.has('TIMING_FUNCTION') && TIMING_FNS.some((f) => part.startsWith(`${f}(`))) {
40+
result.timingFunction = part
41+
seen.add('TIMING_FUNCTION')
42+
} else if (!seen.has('DURATION') && TIME.test(part)) {
43+
result.duration = part
44+
seen.add('DURATION')
45+
} else if (!seen.has('DELAY') && TIME.test(part)) {
46+
result.delay = part
47+
seen.add('DELAY')
48+
} else result.name = part
2949
}
3050

3151
return result

tests/parseAnimationValue.test.js

+35-99
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,22 @@
11
import parseAnimationValue from '../src/util/parseAnimationValue'
2-
import { produce } from './util/produce'
32

43
describe('Tailwind Defaults', () => {
54
it.each([
65
[
76
'spin 1s linear infinite',
8-
{ name: 'spin', duration: '1s', timingFunction: 'linear', iterationCount: 'infinite' },
9-
],
10-
[
11-
'ping 1s cubic-bezier(0, 0, 0.2, 1) infinite',
127
{
13-
name: 'ping',
8+
name: 'spin',
149
duration: '1s',
15-
timingFunction: 'cubic-bezier(0, 0, 0.2, 1)',
10+
timingFunction: 'linear',
1611
iterationCount: 'infinite',
1712
},
1813
],
1914
[
20-
'pulse 2s cubic-bezier(0.4, 0, 0.6) infinite',
15+
'ping 1s cubic-bezier(0, 0, 0.2, 1) infinite',
2116
{
22-
name: 'pulse',
23-
duration: '2s',
24-
timingFunction: 'cubic-bezier(0.4, 0, 0.6)',
17+
name: 'ping',
18+
duration: '1s',
19+
timingFunction: 'cubic-bezier(0, 0, 0.2, 1)',
2520
iterationCount: 'infinite',
2621
},
2722
],
@@ -48,7 +43,12 @@ describe('MDN Examples', () => {
4843
],
4944
[
5045
'slidein 3s linear 1s',
51-
{ delay: '1s', duration: '3s', name: 'slidein', timingFunction: 'linear' },
46+
{
47+
delay: '1s',
48+
duration: '3s',
49+
name: 'slidein',
50+
timingFunction: 'linear',
51+
},
5252
],
5353
['slidein 3s', { duration: '3s', name: 'slidein' }],
5454
])('should be possible to parse: "%s"', (input, expected) => {
@@ -59,44 +59,28 @@ describe('MDN Examples', () => {
5959
describe('duration & delay', () => {
6060
it.each([
6161
// Positive seconds (integer)
62-
['spin 1s 1s linear', { duration: '1s', delay: '1s' }],
6362
['spin 2s 1s linear', { duration: '2s', delay: '1s' }],
64-
['spin 1s 2s linear', { duration: '1s', delay: '2s' }],
6563

6664
// Negative seconds (integer)
67-
['spin -1s -1s linear', { duration: '-1s', delay: '-1s' }],
6865
['spin -2s -1s linear', { duration: '-2s', delay: '-1s' }],
69-
['spin -1s -2s linear', { duration: '-1s', delay: '-2s' }],
7066

7167
// Positive seconds (float)
72-
['spin 1.321s 1.321s linear', { duration: '1.321s', delay: '1.321s' }],
7368
['spin 2.321s 1.321s linear', { duration: '2.321s', delay: '1.321s' }],
74-
['spin 1.321s 2.321s linear', { duration: '1.321s', delay: '2.321s' }],
7569

7670
// Negative seconds (float)
77-
['spin -1.321s -1.321s linear', { duration: '-1.321s', delay: '-1.321s' }],
7871
['spin -2.321s -1.321s linear', { duration: '-2.321s', delay: '-1.321s' }],
79-
['spin -1.321s -2.321s linear', { duration: '-1.321s', delay: '-2.321s' }],
8072

8173
// Positive milliseconds (integer)
82-
['spin 100ms 100ms linear', { duration: '100ms', delay: '100ms' }],
8374
['spin 200ms 100ms linear', { duration: '200ms', delay: '100ms' }],
84-
['spin 100ms 200ms linear', { duration: '100ms', delay: '200ms' }],
8575

8676
// Negative milliseconds (integer)
87-
['spin -100ms -100ms linear', { duration: '-100ms', delay: '-100ms' }],
8877
['spin -200ms -100ms linear', { duration: '-200ms', delay: '-100ms' }],
89-
['spin -100ms -200ms linear', { duration: '-100ms', delay: '-200ms' }],
9078

9179
// Positive milliseconds (float)
92-
['spin 100.321ms 100.321ms linear', { duration: '100.321ms', delay: '100.321ms' }],
9380
['spin 200.321ms 100.321ms linear', { duration: '200.321ms', delay: '100.321ms' }],
94-
['spin 100.321ms 200.321ms linear', { duration: '100.321ms', delay: '200.321ms' }],
9581

9682
// Negative milliseconds (float)
97-
['spin -100.321ms -100.321ms linear', { duration: '-100.321ms', delay: '-100.321ms' }],
9883
['spin -200.321ms -100.321ms linear', { duration: '-200.321ms', delay: '-100.321ms' }],
99-
['spin -100.321ms -200.321ms linear', { duration: '-100.321ms', delay: '-200.321ms' }],
10084
])('should be possible to parse "%s" into %o', (input, { duration, delay }) => {
10185
const parsed = parseAnimationValue(input)
10286
expect(parsed.duration).toEqual(duration)
@@ -127,34 +111,6 @@ describe('iteration count', () => {
127111
)
128112
})
129113

130-
describe('iteration count', () => {
131-
it.each([
132-
// Number
133-
['1 spin 200s 100s linear', '1'],
134-
['spin 2 200s 100s linear', '2'],
135-
['spin 200s 3 100s linear', '3'],
136-
['spin 200s 100s 4 linear', '4'],
137-
['spin 200s 100s linear 5', '5'],
138-
['100 spin 200s 100s linear', '100'],
139-
['spin 200 200s 100s linear', '200'],
140-
['spin 200s 300 100s linear', '300'],
141-
['spin 200s 100s 400 linear', '400'],
142-
['spin 200s 100s linear 500', '500'],
143-
144-
// Infinite
145-
['infinite spin 200s 100s linear', 'infinite'],
146-
['spin infinite 200s 100s linear', 'infinite'],
147-
['spin 200s infinite 100s linear', 'infinite'],
148-
['spin 200s 100s infinite linear', 'infinite'],
149-
['spin 200s 100s linear infinite', 'infinite'],
150-
])(
151-
'should be possible to parse "%s" with an iteraction count of "%s"',
152-
(input, iterationCount) => {
153-
expect(parseAnimationValue(input).iterationCount).toEqual(iterationCount)
154-
}
155-
)
156-
})
157-
158114
describe('multiple animations', () => {
159115
it('should be possible to parse multiple applications at once', () => {
160116
const input = [
@@ -166,7 +122,12 @@ describe('multiple animations', () => {
166122
const parsed = parseAnimationValue(input)
167123
expect(parsed).toHaveLength(3)
168124
expect(parsed).toEqual([
169-
{ name: 'spin', duration: '1s', timingFunction: 'linear', iterationCount: 'infinite' },
125+
{
126+
name: 'spin',
127+
duration: '1s',
128+
timingFunction: 'linear',
129+
iterationCount: 'infinite',
130+
},
170131
{
171132
name: 'ping',
172133
duration: '1s',
@@ -183,46 +144,21 @@ describe('multiple animations', () => {
183144
})
184145
})
185146

186-
describe('randomized crazy big examples', () => {
187-
function reOrder(input, offset = 0) {
188-
return [...input.slice(offset), ...input.slice(0, offset)]
189-
}
190-
191-
it.each(
192-
produce((choose) => {
193-
const direction = choose('normal', 'reverse', 'alternate', 'alternate-reverse')
194-
const playState = choose('running', 'paused')
195-
const fillMode = choose('none', 'forwards', 'backwards', 'both')
196-
const iterationCount = choose('infinite', '1', '100')
197-
const timingFunction = choose(
198-
'linear',
199-
'ease',
200-
'ease-in',
201-
'ease-out',
202-
'ease-in-out',
203-
'cubic-bezier(0, 0, 0.2, 1)',
204-
'steps(4, end)'
205-
)
206-
const name = choose('animation-name-a', 'animation-name-b')
207-
const inputArgs = [direction, playState, fillMode, iterationCount, timingFunction, name]
208-
const orderOffset = choose(...Array(inputArgs.length).keys())
209-
210-
return [
211-
// Input
212-
reOrder(inputArgs, orderOffset).join(' '),
213-
214-
// Output
215-
{
216-
direction,
217-
playState,
218-
fillMode,
219-
iterationCount,
220-
timingFunction,
221-
name,
222-
},
223-
]
224-
})
225-
)('should be possible to parse "%s"', (input, output) => {
226-
expect(parseAnimationValue(input)).toEqual(output)
227-
})
147+
it.each`
148+
input | direction | playState | fillMode | iterationCount | timingFunction | duration | delay | name
149+
${'1s spin 1s infinite'} | ${undefined} | ${undefined} | ${undefined} | ${'infinite'} | ${undefined} | ${'1s'} | ${'1s'} | ${'spin'}
150+
${'infinite infinite 1s 1s'} | ${undefined} | ${undefined} | ${undefined} | ${'infinite'} | ${undefined} | ${'1s'} | ${'1s'} | ${'infinite'}
151+
${'ease 1s ease 1s'} | ${undefined} | ${undefined} | ${undefined} | ${undefined} | ${'ease'} | ${'1s'} | ${'1s'} | ${'ease'}
152+
${'normal paused backwards infinite ease-in 1s 2s name'} | ${'normal'} | ${'paused'} | ${'backwards'} | ${'infinite'} | ${'ease-in'} | ${'1s'} | ${'2s'} | ${'name'}
153+
${'paused backwards infinite ease-in 1s 2s name normal'} | ${'normal'} | ${'paused'} | ${'backwards'} | ${'infinite'} | ${'ease-in'} | ${'1s'} | ${'2s'} | ${'name'}
154+
${'backwards infinite ease-in 1s 2s name normal paused'} | ${'normal'} | ${'paused'} | ${'backwards'} | ${'infinite'} | ${'ease-in'} | ${'1s'} | ${'2s'} | ${'name'}
155+
${'infinite ease-in 1s 2s name normal paused backwards'} | ${'normal'} | ${'paused'} | ${'backwards'} | ${'infinite'} | ${'ease-in'} | ${'1s'} | ${'2s'} | ${'name'}
156+
${'ease-in 1s 2s name normal paused backwards infinite'} | ${'normal'} | ${'paused'} | ${'backwards'} | ${'infinite'} | ${'ease-in'} | ${'1s'} | ${'2s'} | ${'name'}
157+
${'1s 2s name normal paused backwards infinite ease-in'} | ${'normal'} | ${'paused'} | ${'backwards'} | ${'infinite'} | ${'ease-in'} | ${'1s'} | ${'2s'} | ${'name'}
158+
${'2s name normal paused backwards infinite ease-in 1s'} | ${'normal'} | ${'paused'} | ${'backwards'} | ${'infinite'} | ${'ease-in'} | ${'2s'} | ${'1s'} | ${'name'}
159+
${'name normal paused backwards infinite ease-in 1s 2s'} | ${'normal'} | ${'paused'} | ${'backwards'} | ${'infinite'} | ${'ease-in'} | ${'1s'} | ${'2s'} | ${'name'}
160+
${' name normal paused backwards infinite ease-in 1s 2s '} | ${'normal'} | ${'paused'} | ${'backwards'} | ${'infinite'} | ${'ease-in'} | ${'1s'} | ${'2s'} | ${'name'}
161+
`('should parse "$input" correctly', ({ input, ...expected }) => {
162+
let parsed = parseAnimationValue(input)
163+
expect(parsed).toEqual(expected)
228164
})

0 commit comments

Comments
 (0)