|
1 | 1 | import { __unstable__loadDesignSystem } from '@tailwindcss/node'
|
2 |
| -import { expect, test, vi } from 'vitest' |
| 2 | +import { describe, expect, test, vi } from 'vitest' |
3 | 3 | import * as versions from '../../utils/version'
|
4 | 4 | import { migrateCandidate } from './migrate'
|
5 | 5 | vi.spyOn(versions, 'isMajor').mockReturnValue(true)
|
6 | 6 |
|
7 |
| -test('does not replace classes in invalid positions', async () => { |
| 7 | +describe('is-safe-migration', async () => { |
8 | 8 | let designSystem = await __unstable__loadDesignSystem('@import "tailwindcss";', {
|
9 | 9 | base: __dirname,
|
10 | 10 | })
|
11 | 11 |
|
12 |
| - async function shouldNotReplace(example: string, candidate = '!border') { |
| 12 | + test.each([ |
| 13 | + [`let notBorder = !border \n`, '!border'], |
| 14 | + [`{ "foo": !border.something + ""}\n`, '!border'], |
| 15 | + [`<div v-if="something && !border"></div>\n`, '!border'], |
| 16 | + [`<div v-else-if="something && !border"></div>\n`, '!border'], |
| 17 | + [`<div v-show="something && !border"></div>\n`, '!border'], |
| 18 | + [`<div v-if="!border || !border"></div>\n`, '!border'], |
| 19 | + [`<div v-else-if="!border || !border"></div>\n`, '!border'], |
| 20 | + [`<div v-show="!border || !border"></div>\n`, '!border'], |
| 21 | + [`<div v-if="!border"></div>\n`, '!border'], |
| 22 | + [`<div v-else-if="!border"></div>\n`, '!border'], |
| 23 | + [`<div v-show="!border"></div>\n`, '!border'], |
| 24 | + [`<div x-if="!border"></div>\n`, '!border'], |
| 25 | + |
| 26 | + [`let notShadow = shadow \n`, 'shadow'], |
| 27 | + [`{ "foo": shadow.something + ""}\n`, 'shadow'], |
| 28 | + [`<div v-if="something && shadow"></div>\n`, 'shadow'], |
| 29 | + [`<div v-else-if="something && shadow"></div>\n`, 'shadow'], |
| 30 | + [`<div v-show="something && shadow"></div>\n`, 'shadow'], |
| 31 | + [`<div v-if="shadow || shadow"></div>\n`, 'shadow'], |
| 32 | + [`<div v-else-if="shadow || shadow"></div>\n`, 'shadow'], |
| 33 | + [`<div v-show="shadow || shadow"></div>\n`, 'shadow'], |
| 34 | + [`<div v-if="shadow"></div>\n`, 'shadow'], |
| 35 | + [`<div v-else-if="shadow"></div>\n`, 'shadow'], |
| 36 | + [`<div v-show="shadow"></div>\n`, 'shadow'], |
| 37 | + [`<div x-if="shadow"></div>\n`, 'shadow'], |
| 38 | + [`<div style={{filter: 'drop-shadow(30px 10px 4px #4444dd)'}}/>\n`, 'shadow'], |
| 39 | + |
| 40 | + // Next.js Image placeholder cases |
| 41 | + [`<Image placeholder="blur" src="/image.jpg" />`, 'blur'], |
| 42 | + [`<Image placeholder={'blur'} src="/image.jpg" />`, 'blur'], |
| 43 | + [`<Image placeholder={blur} src="/image.jpg" />`, 'blur'], |
| 44 | + |
| 45 | + // https://github.com/tailwindlabs/tailwindcss/issues/17974 |
| 46 | + ['<div v-if="!duration">', '!duration'], |
| 47 | + ['<div :active="!duration">', '!duration'], |
| 48 | + ['<div :active="!visible">', '!visible'], |
| 49 | + |
| 50 | + // Alpine/Livewire wire:… |
| 51 | + ['<x-input.number required="foo" wire:model.blur="coins" />', 'blur'], |
| 52 | + |
| 53 | + // Vue 3 events |
| 54 | + [`emit('blur', props.modelValue)\n`, 'blur'], |
| 55 | + [`$emit('blur', props.modelValue)\n`, 'blur'], |
| 56 | + |
| 57 | + // JavaScript / TypeScript |
| 58 | + [`document.addEventListener('blur',handleBlur)`, 'blur'], |
| 59 | + [`document.addEventListener('blur', handleBlur)`, 'blur'], |
| 60 | + |
| 61 | + [`function foo({ outline = true })`, 'outline'], |
| 62 | + [`function foo({ before = false, outline = true })`, 'outline'], |
| 63 | + [`function foo({before=false,outline=true })`, 'outline'], |
| 64 | + [`function foo({outline=true })`, 'outline'], |
| 65 | + // https://github.com/tailwindlabs/tailwindcss/issues/18675 |
| 66 | + [ |
| 67 | + // With default value |
| 68 | + `function foo({ size = "1.25rem", digit, outline = true, textClass = "", className = "" })`, |
| 69 | + 'outline', |
| 70 | + ], |
| 71 | + [ |
| 72 | + // Without default value |
| 73 | + `function foo({ size = "1.25rem", digit, outline, textClass = "", className = "" })`, |
| 74 | + 'outline', |
| 75 | + ], |
| 76 | + [ |
| 77 | + // As the last argument |
| 78 | + `function foo({ size = "1.25rem", digit, outline })`, |
| 79 | + 'outline', |
| 80 | + ], |
| 81 | + [ |
| 82 | + // As the last argument, but there is techinically another `"` on the same line |
| 83 | + `function foo({ size = "1.25rem", digit, outline }): { return "foo" }`, |
| 84 | + 'outline', |
| 85 | + ], |
| 86 | + [ |
| 87 | + // Tricky quote balancing |
| 88 | + `function foo({ before = "'", outline, after = "'" }): { return "foo" }`, |
| 89 | + 'outline', |
| 90 | + ], |
| 91 | + |
| 92 | + [`function foo(blur, foo)`, 'blur'], |
| 93 | + [`function foo(blur,foo)`, 'blur'], |
| 94 | + ])('does not replace classes in invalid positions #%#', async (example, candidate) => { |
13 | 95 | expect(
|
14 | 96 | await migrateCandidate(designSystem, {}, candidate, {
|
15 | 97 | contents: example,
|
16 | 98 | start: example.indexOf(candidate),
|
17 | 99 | end: example.indexOf(candidate) + candidate.length,
|
18 | 100 | }),
|
19 | 101 | ).toEqual(candidate)
|
20 |
| - } |
21 |
| - |
22 |
| - await shouldNotReplace(`let notBorder = !border \n`) |
23 |
| - await shouldNotReplace(`{ "foo": !border.something + ""}\n`) |
24 |
| - await shouldNotReplace(`<div v-if="something && !border"></div>\n`) |
25 |
| - await shouldNotReplace(`<div v-else-if="something && !border"></div>\n`) |
26 |
| - await shouldNotReplace(`<div v-show="something && !border"></div>\n`) |
27 |
| - await shouldNotReplace(`<div v-if="!border || !border"></div>\n`) |
28 |
| - await shouldNotReplace(`<div v-else-if="!border || !border"></div>\n`) |
29 |
| - await shouldNotReplace(`<div v-show="!border || !border"></div>\n`) |
30 |
| - await shouldNotReplace(`<div v-if="!border"></div>\n`) |
31 |
| - await shouldNotReplace(`<div v-else-if="!border"></div>\n`) |
32 |
| - await shouldNotReplace(`<div v-show="!border"></div>\n`) |
33 |
| - await shouldNotReplace(`<div x-if="!border"></div>\n`) |
34 |
| - |
35 |
| - await shouldNotReplace(`let notShadow = shadow \n`, 'shadow') |
36 |
| - await shouldNotReplace(`{ "foo": shadow.something + ""}\n`, 'shadow') |
37 |
| - await shouldNotReplace(`<div v-if="something && shadow"></div>\n`, 'shadow') |
38 |
| - await shouldNotReplace(`<div v-else-if="something && shadow"></div>\n`, 'shadow') |
39 |
| - await shouldNotReplace(`<div v-show="something && shadow"></div>\n`, 'shadow') |
40 |
| - await shouldNotReplace(`<div v-if="shadow || shadow"></div>\n`, 'shadow') |
41 |
| - await shouldNotReplace(`<div v-else-if="shadow || shadow"></div>\n`, 'shadow') |
42 |
| - await shouldNotReplace(`<div v-show="shadow || shadow"></div>\n`, 'shadow') |
43 |
| - await shouldNotReplace(`<div v-if="shadow"></div>\n`, 'shadow') |
44 |
| - await shouldNotReplace(`<div v-else-if="shadow"></div>\n`, 'shadow') |
45 |
| - await shouldNotReplace(`<div v-show="shadow"></div>\n`, 'shadow') |
46 |
| - await shouldNotReplace(`<div x-if="shadow"></div>\n`, 'shadow') |
47 |
| - await shouldNotReplace( |
48 |
| - `<div style={{filter: 'drop-shadow(30px 10px 4px #4444dd)'}}/>\n`, |
49 |
| - 'shadow', |
50 |
| - ) |
51 |
| - |
52 |
| - // Next.js Image placeholder cases |
53 |
| - await shouldNotReplace(`<Image placeholder="blur" src="/image.jpg" />`, 'blur') |
54 |
| - await shouldNotReplace(`<Image placeholder={'blur'} src="/image.jpg" />`, 'blur') |
55 |
| - await shouldNotReplace(`<Image placeholder={blur} src="/image.jpg" />`, 'blur') |
56 |
| - |
57 |
| - // https://github.com/tailwindlabs/tailwindcss/issues/17974 |
58 |
| - await shouldNotReplace('<div v-if="!duration">', '!duration') |
59 |
| - await shouldNotReplace('<div :active="!duration">', '!duration') |
60 |
| - await shouldNotReplace('<div :active="!visible">', '!visible') |
61 |
| - |
62 |
| - // Alpine/Livewire wire:… |
63 |
| - await shouldNotReplace('<x-input.number required="foo" wire:model.blur="coins" />', 'blur') |
64 |
| - |
65 |
| - // Vue 3 events |
66 |
| - await shouldNotReplace(`emit('blur', props.modelValue)\n`, 'blur') |
67 |
| - await shouldNotReplace(`$emit('blur', props.modelValue)\n`, 'blur') |
| 102 | + }) |
68 | 103 | })
|
0 commit comments