core/api/tailwindcss-master/__tests__/resolveConfig.test.js

2199 lines
47 KiB
JavaScript
Executable File

import { corePluginList } from '../src/corePluginList'
import resolveConfig from '../src/util/resolveConfig'
test('prefix key overrides default prefix', () => {
const userConfig = {
prefix: 'tw-',
}
const defaultConfig = {
prefix: '',
important: false,
separator: ':',
theme: {
screens: {
mobile: '400px',
},
},
variants: {
appearance: ['responsive'],
borderCollapse: [],
borderColors: ['responsive', 'hover', 'focus'],
},
}
const result = resolveConfig([userConfig, defaultConfig])
expect(result).toMatchObject({
prefix: 'tw-',
important: false,
separator: ':',
theme: {
screens: {
mobile: '400px',
},
},
variants: {
appearance: ['responsive'],
borderCollapse: [],
borderColors: ['responsive', 'hover', 'focus'],
},
})
})
test('important key overrides default important', () => {
const userConfig = {
important: true,
}
const defaultConfig = {
prefix: '',
important: false,
separator: ':',
theme: {
screens: {
mobile: '400px',
},
},
variants: {
appearance: ['responsive'],
borderCollapse: [],
borderColors: ['responsive', 'hover', 'focus'],
},
}
const result = resolveConfig([userConfig, defaultConfig])
expect(result).toMatchObject({
prefix: '',
important: true,
separator: ':',
theme: {
screens: {
mobile: '400px',
},
},
variants: {
appearance: ['responsive'],
borderCollapse: [],
borderColors: ['responsive', 'hover', 'focus'],
},
})
})
test('important (selector) key overrides default important', () => {
const userConfig = {
important: '#app',
}
const defaultConfig = {
prefix: '',
important: false,
separator: ':',
theme: {
screens: {
mobile: '400px',
},
},
variants: {
appearance: ['responsive'],
borderCollapse: [],
borderColors: ['responsive', 'hover', 'focus'],
},
}
const result = resolveConfig([userConfig, defaultConfig])
expect(result).toMatchObject({
prefix: '',
important: '#app',
separator: ':',
theme: {
screens: {
mobile: '400px',
},
},
variants: {
appearance: ['responsive'],
borderCollapse: [],
borderColors: ['responsive', 'hover', 'focus'],
},
})
})
test('separator key overrides default separator', () => {
const userConfig = {
separator: '__',
}
const defaultConfig = {
prefix: '',
important: false,
separator: ':',
theme: {
screens: {
mobile: '400px',
},
},
variants: {
appearance: ['responsive'],
borderCollapse: [],
borderColors: ['responsive', 'hover', 'focus'],
},
}
const result = resolveConfig([userConfig, defaultConfig])
expect(result).toMatchObject({
prefix: '',
important: false,
separator: '__',
theme: {
screens: {
mobile: '400px',
},
},
variants: {
appearance: ['responsive'],
borderCollapse: [],
borderColors: ['responsive', 'hover', 'focus'],
},
})
})
test('theme key is merged instead of replaced', () => {
const userConfig = {
theme: {
screens: {
mobile: '400px',
},
},
}
const defaultConfig = {
prefix: '-',
important: false,
separator: ':',
theme: {
colors: {
'grey-darker': '#606f7b',
'grey-dark': '#8795a1',
grey: '#b8c2cc',
'grey-light': '#dae1e7',
'grey-lighter': '#f1f5f8',
},
fonts: {
sans: ['system-ui', 'BlinkMacSystemFont', '-apple-system', 'Roboto', 'sans-serif'],
serif: ['Constantia', 'Lucida Bright', 'Georgia', 'serif'],
},
screens: {
sm: '500px',
md: '750px',
lg: '1000px',
},
},
variants: {
appearance: ['responsive'],
borderCollapse: [],
borderColors: ['responsive', 'hover', 'focus'],
},
}
const result = resolveConfig([userConfig, defaultConfig])
expect(result).toMatchObject({
prefix: '-',
important: false,
separator: ':',
theme: {
colors: {
'grey-darker': '#606f7b',
'grey-dark': '#8795a1',
grey: '#b8c2cc',
'grey-light': '#dae1e7',
'grey-lighter': '#f1f5f8',
},
fonts: {
sans: ['system-ui', 'BlinkMacSystemFont', '-apple-system', 'Roboto', 'sans-serif'],
serif: ['Constantia', 'Lucida Bright', 'Georgia', 'serif'],
},
screens: {
mobile: '400px',
},
},
variants: {
appearance: ['responsive'],
borderCollapse: [],
borderColors: ['responsive', 'hover', 'focus'],
},
})
})
test('theme key is deeply merged instead of replaced', () => {
const userConfig = {
theme: {
extend: {
colors: {
grey: {
darker: '#606f7b',
dark: '#8795a1',
},
},
},
},
}
const defaultConfig = {
prefix: '-',
important: false,
separator: ':',
theme: {
colors: {
grey: {
grey: '#b8c2cc',
light: '#dae1e7',
lighter: '#f1f5f8',
},
},
},
}
const result = resolveConfig([userConfig, defaultConfig])
expect(result).toMatchObject({
prefix: '-',
important: false,
separator: ':',
theme: {
colors: {
grey: {
darker: '#606f7b',
dark: '#8795a1',
grey: '#b8c2cc',
light: '#dae1e7',
lighter: '#f1f5f8',
},
},
},
})
})
test('variants key is merged instead of replaced', () => {
const userConfig = {
variants: {
backgroundAttachment: [],
borderColors: ['responsive', 'hover', 'focus', 'active'],
},
}
const defaultConfig = {
prefix: '-',
important: false,
separator: ':',
theme: {
colors: {
'grey-darker': '#606f7b',
'grey-dark': '#8795a1',
grey: '#b8c2cc',
'grey-light': '#dae1e7',
'grey-lighter': '#f1f5f8',
},
fonts: {
sans: ['system-ui', 'BlinkMacSystemFont', '-apple-system', 'Roboto', 'sans-serif'],
serif: ['Constantia', 'Lucida Bright', 'Georgia', 'serif'],
},
screens: {
sm: '500px',
md: '750px',
lg: '1000px',
},
},
variants: {
appearance: ['responsive'],
backgroundAttachment: ['responsive'],
borderCollapse: [],
borderColors: ['responsive', 'hover', 'focus'],
borderRadius: ['responsive'],
},
}
const result = resolveConfig([userConfig, defaultConfig])
expect(result).toMatchObject({
prefix: '-',
important: false,
separator: ':',
theme: {
colors: {
'grey-darker': '#606f7b',
'grey-dark': '#8795a1',
grey: '#b8c2cc',
'grey-light': '#dae1e7',
'grey-lighter': '#f1f5f8',
},
fonts: {
sans: ['system-ui', 'BlinkMacSystemFont', '-apple-system', 'Roboto', 'sans-serif'],
serif: ['Constantia', 'Lucida Bright', 'Georgia', 'serif'],
},
screens: {
sm: '500px',
md: '750px',
lg: '1000px',
},
},
variants: {
appearance: ['responsive'],
backgroundAttachment: [],
borderCollapse: [],
borderColors: ['responsive', 'hover', 'focus', 'active'],
borderRadius: ['responsive'],
},
})
})
test('a global variants list replaces the default', () => {
const userConfig = {
variants: ['responsive', 'hover', 'focus', 'active'],
}
const defaultConfig = {
prefix: '-',
important: false,
separator: ':',
theme: {
colors: {
'grey-darker': '#606f7b',
'grey-dark': '#8795a1',
grey: '#b8c2cc',
'grey-light': '#dae1e7',
'grey-lighter': '#f1f5f8',
},
fonts: {
sans: ['system-ui', 'BlinkMacSystemFont', '-apple-system', 'Roboto', 'sans-serif'],
serif: ['Constantia', 'Lucida Bright', 'Georgia', 'serif'],
},
screens: {
sm: '500px',
md: '750px',
lg: '1000px',
},
},
variants: {
appearance: ['responsive'],
backgroundAttachment: ['responsive'],
borderCollapse: [],
borderColors: ['responsive', 'hover', 'focus'],
borderRadius: ['responsive'],
},
}
const result = resolveConfig([userConfig, defaultConfig])
expect(result).toMatchObject({
prefix: '-',
important: false,
separator: ':',
theme: {
colors: {
'grey-darker': '#606f7b',
'grey-dark': '#8795a1',
grey: '#b8c2cc',
'grey-light': '#dae1e7',
'grey-lighter': '#f1f5f8',
},
fonts: {
sans: ['system-ui', 'BlinkMacSystemFont', '-apple-system', 'Roboto', 'sans-serif'],
serif: ['Constantia', 'Lucida Bright', 'Georgia', 'serif'],
},
screens: {
sm: '500px',
md: '750px',
lg: '1000px',
},
},
variants: ['responsive', 'hover', 'focus', 'active'],
})
})
test('missing top level keys are pulled from the default config', () => {
const userConfig = {}
const defaultConfig = {
prefix: '-',
important: false,
separator: ':',
theme: {
colors: { green: '#00ff00' },
screens: {
mobile: '400px',
},
},
variants: {
appearance: ['responsive'],
borderCollapse: [],
borderColors: ['responsive', 'hover', 'focus'],
},
}
const result = resolveConfig([userConfig, defaultConfig])
expect(result).toMatchObject({
prefix: '-',
important: false,
separator: ':',
theme: {
colors: { green: '#00ff00' },
screens: {
mobile: '400px',
},
},
variants: {
appearance: ['responsive'],
borderCollapse: [],
borderColors: ['responsive', 'hover', 'focus'],
},
})
})
test('functions in the default theme section are lazily evaluated', () => {
const userConfig = {
theme: {
colors: {
red: 'red',
green: 'green',
blue: 'blue',
},
},
}
const defaultConfig = {
prefix: '-',
important: false,
separator: ':',
theme: {
colors: {
cyan: 'cyan',
magenta: 'magenta',
yellow: 'yellow',
},
backgroundColors: (theme) => theme('colors'),
textColors: (theme) => theme('colors'),
},
variants: {
backgroundColors: ['responsive', 'hover', 'focus'],
textColors: ['responsive', 'hover', 'focus'],
},
}
const result = resolveConfig([userConfig, defaultConfig])
expect(result).toMatchObject({
prefix: '-',
important: false,
separator: ':',
theme: {
colors: {
red: 'red',
green: 'green',
blue: 'blue',
},
backgroundColors: {
red: 'red',
green: 'green',
blue: 'blue',
},
textColors: {
red: 'red',
green: 'green',
blue: 'blue',
},
},
variants: {
backgroundColors: ['responsive', 'hover', 'focus'],
textColors: ['responsive', 'hover', 'focus'],
},
})
})
test('functions in the user theme section are lazily evaluated', () => {
const userConfig = {
theme: {
colors: {
red: 'red',
green: 'green',
blue: 'blue',
},
backgroundColors: (theme) => ({
...theme('colors'),
customBackground: '#bada55',
}),
textColors: (theme) => ({
...theme('colors'),
customText: '#facade',
}),
},
}
const defaultConfig = {
prefix: '-',
important: false,
separator: ':',
theme: {
colors: {
cyan: 'cyan',
magenta: 'magenta',
yellow: 'yellow',
},
backgroundColors: ({ colors }) => colors,
textColors: ({ colors }) => colors,
},
variants: {
backgroundColors: ['responsive', 'hover', 'focus'],
textColors: ['responsive', 'hover', 'focus'],
},
}
const result = resolveConfig([userConfig, defaultConfig])
expect(result).toMatchObject({
prefix: '-',
important: false,
separator: ':',
theme: {
colors: {
red: 'red',
green: 'green',
blue: 'blue',
},
backgroundColors: {
red: 'red',
green: 'green',
blue: 'blue',
customBackground: '#bada55',
},
textColors: {
red: 'red',
green: 'green',
blue: 'blue',
customText: '#facade',
},
},
variants: {
backgroundColors: ['responsive', 'hover', 'focus'],
textColors: ['responsive', 'hover', 'focus'],
},
})
})
test('theme values in the extend section extend the existing theme', () => {
const userConfig = {
theme: {
extend: {
opacity: {
25: '25',
75: '.75',
},
backgroundColors: {
customBackground: '#bada55',
},
},
},
}
const defaultConfig = {
prefix: '-',
important: false,
separator: ':',
theme: {
colors: {
cyan: 'cyan',
magenta: 'magenta',
yellow: 'yellow',
},
opacity: {
0: '0',
50: '.5',
100: '1',
},
backgroundColors: (theme) => theme('colors'),
},
variants: {
backgroundColors: ['responsive', 'hover', 'focus'],
opacity: ['responsive', 'hover', 'focus'],
},
}
const result = resolveConfig([userConfig, defaultConfig])
expect(result).toMatchObject({
prefix: '-',
important: false,
separator: ':',
theme: {
colors: {
cyan: 'cyan',
magenta: 'magenta',
yellow: 'yellow',
},
opacity: {
0: '0',
50: '.5',
100: '1',
25: '25',
75: '.75',
},
backgroundColors: {
cyan: 'cyan',
magenta: 'magenta',
yellow: 'yellow',
customBackground: '#bada55',
},
},
variants: {
backgroundColors: ['responsive', 'hover', 'focus'],
opacity: ['responsive', 'hover', 'focus'],
},
})
})
test('theme values in the extend section extend the user theme', () => {
const userConfig = {
theme: {
opacity: {
0: '0',
20: '.2',
40: '.4',
},
height: (theme) => theme('width'),
extend: {
opacity: {
60: '.6',
80: '.8',
100: '1',
},
height: {
customHeight: '500vh',
},
},
},
}
const defaultConfig = {
prefix: '-',
important: false,
separator: ':',
theme: {
opacity: {
0: '0',
50: '.5',
100: '1',
},
height: {
0: 0,
full: '100%',
},
width: {
0: 0,
1: '.25rem',
2: '.5rem',
3: '.75rem',
4: '1rem',
},
},
variants: {
opacity: ['responsive', 'hover', 'focus'],
height: ['responsive'],
width: ['responsive'],
},
}
const result = resolveConfig([userConfig, defaultConfig])
expect(result).toMatchObject({
prefix: '-',
important: false,
separator: ':',
theme: {
opacity: {
0: '0',
20: '.2',
40: '.4',
60: '.6',
80: '.8',
100: '1',
},
height: {
0: 0,
1: '.25rem',
2: '.5rem',
3: '.75rem',
4: '1rem',
customHeight: '500vh',
},
width: {
0: 0,
1: '.25rem',
2: '.5rem',
3: '.75rem',
4: '1rem',
},
},
variants: {
opacity: ['responsive', 'hover', 'focus'],
height: ['responsive'],
width: ['responsive'],
},
})
})
test('theme values in the extend section can extend values that are depended on lazily', () => {
const userConfig = {
theme: {
extend: {
colors: {
red: 'red',
green: 'green',
blue: 'blue',
},
backgroundColors: {
customBackground: '#bada55',
},
},
},
}
const defaultConfig = {
prefix: '-',
important: false,
separator: ':',
theme: {
colors: {
cyan: 'cyan',
magenta: 'magenta',
yellow: 'yellow',
},
backgroundColors: (theme) => theme('colors'),
},
variants: {
backgroundColors: ['responsive', 'hover', 'focus'],
},
}
const result = resolveConfig([userConfig, defaultConfig])
expect(result).toMatchObject({
prefix: '-',
important: false,
separator: ':',
theme: {
colors: {
cyan: 'cyan',
magenta: 'magenta',
yellow: 'yellow',
red: 'red',
green: 'green',
blue: 'blue',
},
backgroundColors: {
cyan: 'cyan',
magenta: 'magenta',
yellow: 'yellow',
red: 'red',
green: 'green',
blue: 'blue',
customBackground: '#bada55',
},
},
variants: {
backgroundColors: ['responsive', 'hover', 'focus'],
},
})
})
test('theme values in the extend section are not deeply merged when they are simple arrays', () => {
const userConfig = {
theme: {
extend: {
fonts: {
sans: ['Comic Sans'],
},
},
},
}
const defaultConfig = {
prefix: '-',
important: false,
separator: ':',
theme: {
fonts: {
sans: ['system-ui', 'Helvetica Neue', 'sans-serif'],
serif: ['Constantia', 'Georgia', 'serif'],
mono: ['Menlo', 'Courier New', 'monospace'],
},
},
variants: {
fonts: ['responsive'],
},
}
const result = resolveConfig([userConfig, defaultConfig])
expect(result).toMatchObject({
prefix: '-',
important: false,
separator: ':',
theme: {
fonts: {
sans: ['Comic Sans'],
serif: ['Constantia', 'Georgia', 'serif'],
mono: ['Menlo', 'Courier New', 'monospace'],
},
},
variants: {
fonts: ['responsive'],
},
})
})
test('theme values in the extend section are deeply merged, when they are arrays of objects', () => {
const userConfig = {
theme: {
extend: {
typography: {
ArrayArray: {
css: [{ a: { backgroundColor: 'red' } }, { a: { color: 'green' } }],
},
ObjectArray: {
css: { a: { backgroundColor: 'red' } },
},
ArrayObject: {
css: [{ a: { backgroundColor: 'red' } }, { a: { color: 'green' } }],
},
},
},
},
}
const defaultConfig = {
prefix: '-',
important: false,
separator: ':',
theme: {
typography: {
ArrayArray: {
css: [{ a: { underline: 'none' } }],
},
ObjectArray: {
css: [{ a: { underline: 'none' } }],
},
ArrayObject: {
css: { a: { underline: 'none' } },
},
},
},
variants: {},
}
const result = resolveConfig([userConfig, defaultConfig])
expect(result).toMatchObject({
prefix: '-',
important: false,
separator: ':',
theme: {
typography: {
ArrayArray: {
css: [
{ a: { underline: 'none' } },
{ a: { backgroundColor: 'red' } },
{ a: { color: 'green' } },
],
},
ObjectArray: {
css: [{ a: { underline: 'none' } }, { a: { backgroundColor: 'red' } }],
},
ArrayObject: {
css: [
{ a: { underline: 'none' } },
{ a: { backgroundColor: 'red' } },
{ a: { color: 'green' } },
],
},
},
},
variants: {},
})
})
test('the theme function can use a default value if the key is missing', () => {
const userConfig = {
theme: {
colors: {
red: 'red',
green: 'green',
blue: 'blue',
},
},
}
const defaultConfig = {
prefix: '-',
important: false,
separator: ':',
theme: {
colors: {
cyan: 'cyan',
magenta: 'magenta',
yellow: 'yellow',
},
borderColor: (theme) => ({
default: theme('colors.gray', 'currentColor'),
...theme('colors'),
}),
},
variants: {
borderColor: ['responsive', 'hover', 'focus'],
},
}
const result = resolveConfig([userConfig, defaultConfig])
expect(result).toMatchObject({
prefix: '-',
important: false,
separator: ':',
theme: {
colors: {
red: 'red',
green: 'green',
blue: 'blue',
},
borderColor: {
default: 'currentColor',
red: 'red',
green: 'green',
blue: 'blue',
},
},
variants: {
borderColor: ['responsive', 'hover', 'focus'],
},
})
})
test('the theme function can resolve function values', () => {
const userConfig = {
theme: {
textColor: (theme) => ({
lime: 'lime',
...theme('colors'),
}),
backgroundColor: (theme) => ({
orange: 'orange',
...theme('textColor'),
}),
borderColor: (theme) => theme('backgroundColor'),
},
}
const defaultConfig = {
prefix: '-',
important: false,
separator: ':',
theme: {
colors: {
red: 'red',
green: 'green',
blue: 'blue',
},
},
variants: {
backgroundColor: ['responsive', 'hover', 'focus'],
borderColor: ['responsive', 'hover', 'focus'],
},
}
const result = resolveConfig([userConfig, defaultConfig])
expect(result).toMatchObject({
prefix: '-',
important: false,
separator: ':',
theme: {
colors: {
red: 'red',
green: 'green',
blue: 'blue',
},
textColor: {
lime: 'lime',
red: 'red',
green: 'green',
blue: 'blue',
},
backgroundColor: {
lime: 'lime',
orange: 'orange',
red: 'red',
green: 'green',
blue: 'blue',
},
borderColor: {
lime: 'lime',
orange: 'orange',
red: 'red',
green: 'green',
blue: 'blue',
},
},
variants: {
backgroundColor: ['responsive', 'hover', 'focus'],
borderColor: ['responsive', 'hover', 'focus'],
},
})
})
test('the theme function can resolve deep function values', () => {
const userConfig = {
theme: {
minWidth: (theme) => ({
'1/3': theme('width.1/3'),
}),
},
}
const defaultConfig = {
prefix: '-',
important: false,
separator: ':',
theme: {
spacing: {
0: '0',
},
width: (theme) => ({
...theme('spacing'),
'1/3': '33.33333%',
}),
},
variants: {
backgroundColor: ['responsive', 'hover', 'focus'],
borderColor: ['responsive', 'hover', 'focus'],
},
}
const result = resolveConfig([userConfig, defaultConfig])
expect(result).toMatchObject({
prefix: '-',
important: false,
separator: ':',
theme: {
spacing: {
0: '0',
},
width: {
0: '0',
'1/3': '33.33333%',
},
minWidth: {
'1/3': '33.33333%',
},
},
variants: {
backgroundColor: ['responsive', 'hover', 'focus'],
borderColor: ['responsive', 'hover', 'focus'],
},
})
})
test('theme values in the extend section are lazily evaluated', () => {
const userConfig = {
theme: {
colors: {
red: 'red',
green: 'green',
blue: 'blue',
},
extend: {
colors: {
orange: 'orange',
},
borderColor: (theme) => ({
foo: theme('colors.orange'),
bar: theme('colors.red'),
}),
},
},
}
const defaultConfig = {
prefix: '-',
important: false,
separator: ':',
theme: {
colors: {
cyan: 'cyan',
magenta: 'magenta',
yellow: 'yellow',
},
borderColor: (theme) => ({
default: theme('colors.yellow', 'currentColor'),
...theme('colors'),
}),
},
variants: {
borderColor: ['responsive', 'hover', 'focus'],
},
}
const result = resolveConfig([userConfig, defaultConfig])
expect(result).toMatchObject({
prefix: '-',
important: false,
separator: ':',
theme: {
colors: {
orange: 'orange',
red: 'red',
green: 'green',
blue: 'blue',
},
borderColor: {
default: 'currentColor',
foo: 'orange',
bar: 'red',
orange: 'orange',
red: 'red',
green: 'green',
blue: 'blue',
},
},
variants: {
borderColor: ['responsive', 'hover', 'focus'],
},
})
})
test('lazily evaluated values have access to the config utils', () => {
const userConfig = {
theme: {
inset: (theme) => theme('margin'),
shift: (theme, { negative }) => ({
...theme('spacing'),
...negative(theme('spacing')),
}),
extend: {
nudge: (theme, { negative }) => ({
...theme('spacing'),
...negative(theme('spacing')),
}),
},
},
}
const defaultConfig = {
prefix: '-',
important: false,
separator: ':',
theme: {
spacing: {
1: '1px',
2: '2px',
3: '3px',
4: '4px',
},
margin: (theme, { negative }) => ({
...theme('spacing'),
...negative(theme('spacing')),
}),
},
variants: {},
}
const result = resolveConfig([userConfig, defaultConfig])
expect(result).toMatchObject({
prefix: '-',
important: false,
separator: ':',
theme: {
spacing: {
1: '1px',
2: '2px',
3: '3px',
4: '4px',
},
inset: {
'-1': '-1px',
'-2': '-2px',
'-3': '-3px',
'-4': '-4px',
1: '1px',
2: '2px',
3: '3px',
4: '4px',
},
margin: {
'-1': '-1px',
'-2': '-2px',
'-3': '-3px',
'-4': '-4px',
1: '1px',
2: '2px',
3: '3px',
4: '4px',
},
shift: {
'-1': '-1px',
'-2': '-2px',
'-3': '-3px',
'-4': '-4px',
1: '1px',
2: '2px',
3: '3px',
4: '4px',
},
nudge: {
'-1': '-1px',
'-2': '-2px',
'-3': '-3px',
'-4': '-4px',
1: '1px',
2: '2px',
3: '3px',
4: '4px',
},
},
variants: {},
})
})
test('the original theme is not mutated', () => {
const userConfig = {
theme: {
extend: {
colors: {
orange: 'orange',
},
},
},
variants: {
borderColor: ['responsive', 'hover'],
},
}
const defaultConfig = {
prefix: '-',
important: false,
separator: ':',
theme: {
colors: {
cyan: 'cyan',
magenta: 'magenta',
yellow: 'yellow',
},
},
variants: {
backgroundColor: ['responsive', 'hover', 'focus'],
},
}
resolveConfig([userConfig, defaultConfig])
expect(userConfig).toEqual({
theme: {
extend: {
colors: {
orange: 'orange',
},
},
},
variants: {
borderColor: ['responsive', 'hover'],
},
})
})
test('custom properties are multiplied by -1 for negative values', () => {
const userConfig = {
theme: {
spacing: {
1: '1px',
2: '2px',
3: '3px',
4: '4px',
foo: 'var(--foo)',
bar: 'var(--bar, 500px)',
baz: 'calc(50% - 10px)',
},
margin: (theme, { negative }) => ({
...theme('spacing'),
...negative(theme('spacing')),
}),
},
}
const defaultConfig = {
prefix: '-',
important: false,
separator: ':',
theme: {},
variants: {},
}
const result = resolveConfig([userConfig, defaultConfig])
expect(result).toMatchObject({
prefix: '-',
important: false,
separator: ':',
theme: {
spacing: {
1: '1px',
2: '2px',
3: '3px',
4: '4px',
foo: 'var(--foo)',
bar: 'var(--bar, 500px)',
baz: 'calc(50% - 10px)',
},
margin: {
1: '1px',
2: '2px',
3: '3px',
4: '4px',
foo: 'var(--foo)',
bar: 'var(--bar, 500px)',
baz: 'calc(50% - 10px)',
'-1': '-1px',
'-2': '-2px',
'-3': '-3px',
'-4': '-4px',
'-foo': 'calc(var(--foo) * -1)',
'-bar': 'calc(var(--bar, 500px) * -1)',
'-baz': 'calc(-50% - -10px)',
},
},
variants: {},
})
})
test('more than two config objects can be resolved', () => {
const firstConfig = {
theme: {
extend: {
fontFamily: () => ({
code: ['Menlo', 'monospace'],
}),
colors: {
red: 'red',
},
backgroundColor: {
customBackgroundOne: '#bada55',
},
textDecorationColor: {
orange: 'orange',
},
},
},
}
const secondConfig = {
prefix: '-',
important: false,
separator: ':',
theme: {
extend: {
fontFamily: {
quote: ['Helvetica', 'serif'],
},
colors: {
green: 'green',
},
backgroundColor: {
customBackgroundTwo: '#facade',
},
textDecorationColor: (theme) => theme('colors'),
},
},
}
const thirdConfig = {
prefix: '-',
important: false,
separator: ':',
theme: {
extend: {
fontFamily: {
hero: ['Futura', 'sans-serif'],
},
colors: {
pink: 'pink',
},
backgroundColor: () => ({
customBackgroundThree: '#c0ffee',
}),
textDecorationColor: {
lime: 'lime',
},
},
},
}
const defaultConfig = {
prefix: '-',
important: false,
separator: ':',
theme: {
fontFamily: {
body: ['Arial', 'sans-serif'],
display: ['Georgia', 'serif'],
},
colors: {
blue: 'blue',
},
backgroundColor: (theme) => theme('colors'),
},
variants: {
backgroundColor: ['responsive', 'hover', 'focus'],
},
}
const result = resolveConfig([firstConfig, secondConfig, thirdConfig, defaultConfig])
expect(result).toMatchObject({
prefix: '-',
important: false,
separator: ':',
theme: {
fontFamily: {
body: ['Arial', 'sans-serif'],
display: ['Georgia', 'serif'],
code: ['Menlo', 'monospace'],
quote: ['Helvetica', 'serif'],
hero: ['Futura', 'sans-serif'],
},
colors: {
red: 'red',
green: 'green',
blue: 'blue',
pink: 'pink',
},
backgroundColor: {
red: 'red',
green: 'green',
blue: 'blue',
pink: 'pink',
customBackgroundOne: '#bada55',
customBackgroundTwo: '#facade',
customBackgroundThree: '#c0ffee',
},
textDecorationColor: {
red: 'red',
green: 'green',
blue: 'blue',
pink: 'pink',
orange: 'orange',
lime: 'lime',
},
},
variants: {
backgroundColor: ['responsive', 'hover', 'focus'],
},
})
})
test('plugin config modifications are applied', () => {
const userConfig = {
plugins: [
{
config: {
prefix: 'tw-',
},
},
],
}
const defaultConfig = {
prefix: '',
important: false,
separator: ':',
theme: {
screens: {
mobile: '400px',
},
},
variants: {
appearance: ['responsive'],
borderCollapse: [],
borderColors: ['responsive', 'hover', 'focus'],
},
}
const result = resolveConfig([userConfig, defaultConfig])
expect(result).toMatchObject({
prefix: 'tw-',
important: false,
separator: ':',
theme: {
screens: {
mobile: '400px',
},
},
variants: {
appearance: ['responsive'],
borderCollapse: [],
borderColors: ['responsive', 'hover', 'focus'],
},
plugins: userConfig.plugins,
})
})
test('user config takes precedence over plugin config modifications', () => {
const userConfig = {
prefix: 'user-',
plugins: [
{
config: {
prefix: 'tw-',
},
},
],
}
const defaultConfig = {
prefix: '',
important: false,
separator: ':',
theme: {
screens: {
mobile: '400px',
},
},
variants: {
appearance: ['responsive'],
borderCollapse: [],
borderColors: ['responsive', 'hover', 'focus'],
},
}
const result = resolveConfig([userConfig, defaultConfig])
expect(result).toMatchObject({
prefix: 'user-',
important: false,
separator: ':',
theme: {
screens: {
mobile: '400px',
},
},
variants: {
appearance: ['responsive'],
borderCollapse: [],
borderColors: ['responsive', 'hover', 'focus'],
},
plugins: userConfig.plugins,
})
})
test('plugin config can register plugins that also have config', () => {
const userConfig = {
plugins: [
{
config: {
prefix: 'tw-',
plugins: [
{
config: {
important: true,
},
},
{
config: {
separator: '__',
},
},
],
},
handler() {},
},
],
}
const defaultConfig = {
prefix: '',
important: false,
separator: ':',
theme: {
screens: {
mobile: '400px',
},
},
variants: {
appearance: ['responsive'],
borderCollapse: [],
borderColors: ['responsive', 'hover', 'focus'],
},
}
const result = resolveConfig([userConfig, defaultConfig])
expect(result).toMatchObject({
prefix: 'tw-',
important: true,
separator: '__',
theme: {
screens: {
mobile: '400px',
},
},
variants: {
appearance: ['responsive'],
borderCollapse: [],
borderColors: ['responsive', 'hover', 'focus'],
},
plugins: userConfig.plugins,
})
})
test('plugin configs take precedence over plugin configs registered by that plugin', () => {
const userConfig = {
plugins: [
{
config: {
prefix: 'outer-',
plugins: [
{
config: {
prefix: 'inner-',
},
},
],
},
handler() {},
},
],
}
const defaultConfig = {
prefix: '',
important: false,
separator: ':',
theme: {
screens: {
mobile: '400px',
},
},
variants: {
appearance: ['responsive'],
borderCollapse: [],
borderColors: ['responsive', 'hover', 'focus'],
},
}
const result = resolveConfig([userConfig, defaultConfig])
expect(result).toMatchObject({
prefix: 'outer-',
important: false,
separator: ':',
theme: {
screens: {
mobile: '400px',
},
},
variants: {
appearance: ['responsive'],
borderCollapse: [],
borderColors: ['responsive', 'hover', 'focus'],
},
plugins: userConfig.plugins,
})
})
test('plugin theme extensions are added even if user overrides top-level theme config', () => {
const userConfig = {
theme: {
width: {
'1px': '1px',
},
},
plugins: [
{
config: {
theme: {
extend: {
width: {
'2px': '2px',
'3px': '3px',
},
},
},
},
handler() {},
},
],
}
const defaultConfig = {
prefix: '',
important: false,
separator: ':',
theme: {
width: {
sm: '1rem',
md: '2rem',
lg: '3rem',
},
screens: {
mobile: '400px',
},
},
variants: {
appearance: ['responsive'],
borderCollapse: [],
borderColors: ['responsive', 'hover', 'focus'],
},
}
const result = resolveConfig([userConfig, defaultConfig])
expect(result).toMatchObject({
prefix: '',
important: false,
separator: ':',
theme: {
width: {
'1px': '1px',
'2px': '2px',
'3px': '3px',
},
screens: {
mobile: '400px',
},
},
variants: {
appearance: ['responsive'],
borderCollapse: [],
borderColors: ['responsive', 'hover', 'focus'],
},
plugins: userConfig.plugins,
})
})
test('user theme extensions take precedence over plugin theme extensions with the same key', () => {
const userConfig = {
theme: {
extend: {
width: {
xl: '6rem',
},
},
},
plugins: [
{
config: {
theme: {
extend: {
width: {
xl: '4rem',
},
},
},
},
handler() {},
},
],
}
const defaultConfig = {
prefix: '',
important: false,
separator: ':',
theme: {
width: {
sm: '1rem',
md: '2rem',
lg: '3rem',
},
screens: {
mobile: '400px',
},
},
variants: {
appearance: ['responsive'],
borderCollapse: [],
borderColors: ['responsive', 'hover', 'focus'],
},
}
const result = resolveConfig([userConfig, defaultConfig])
expect(result).toMatchObject({
prefix: '',
important: false,
separator: ':',
theme: {
width: {
sm: '1rem',
md: '2rem',
lg: '3rem',
xl: '6rem',
},
screens: {
mobile: '400px',
},
},
variants: {
appearance: ['responsive'],
borderCollapse: [],
borderColors: ['responsive', 'hover', 'focus'],
},
plugins: userConfig.plugins,
})
})
test('variants can be extended', () => {
const userConfig = {
variants: {
borderColor: ({ after }) => after(['group-focus'], 'hover'),
extend: {
backgroundColor: ['active', 'disabled', 'group-hover'],
},
},
}
const otherConfig = {
variants: {
extend: {
textColor: ['hover', 'focus-within'],
},
},
}
const defaultConfig = {
prefix: '',
important: false,
separator: ':',
theme: {},
variants: {
borderColor: ['hover', 'focus'],
backgroundColor: ['responsive', 'hover', 'focus'],
textColor: ['responsive', 'focus'],
},
}
const result = resolveConfig([userConfig, otherConfig, defaultConfig])
expect(result).toMatchObject({
variants: {
borderColor: ['hover', 'group-focus', 'focus'],
backgroundColor: ['responsive', 'group-hover', 'hover', 'focus', 'active', 'disabled'],
textColor: ['responsive', 'focus-within', 'hover', 'focus'],
},
})
})
test('extensions are applied in the right order', () => {
const userConfig = {
theme: {
extend: {
colors: {
grey: {
light: '#eee',
},
},
},
},
}
const otherConfig = {
theme: {
extend: {
colors: {
grey: {
light: '#ddd',
darker: '#111',
},
},
},
},
}
const anotherConfig = {
theme: {
extend: {
colors: {
grey: {
darker: '#222',
},
},
},
},
}
const defaultConfig = {
theme: {
colors: {
grey: {
light: '#ccc',
dark: '#333',
},
},
},
}
const result = resolveConfig([userConfig, otherConfig, anotherConfig, defaultConfig])
expect(result).toMatchObject({
theme: {
colors: {
grey: {
light: '#eee',
dark: '#333',
darker: '#111',
},
},
},
})
})
test('variant sort order can be customized', () => {
const userConfig = {
variantOrder: [
'disabled',
'focus',
'group-hover',
'focus-within',
'active',
'hover',
'responsive',
],
variants: {
borderColor: ({ after }) => after(['group-focus'], 'hover'),
extend: {
backgroundColor: ['active', 'disabled', 'group-hover'],
},
},
}
const otherConfig = {
variants: {
extend: {
textColor: ['hover', 'focus-within'],
},
},
}
const defaultConfig = {
prefix: '',
important: false,
separator: ':',
theme: {},
variants: {
borderColor: ['hover', 'focus'],
backgroundColor: ['responsive', 'hover', 'focus'],
textColor: ['responsive', 'focus'],
},
}
const result = resolveConfig([userConfig, otherConfig, defaultConfig])
expect(result).toMatchObject({
variants: {
borderColor: ['hover', 'group-focus', 'focus'],
backgroundColor: ['disabled', 'focus', 'group-hover', 'active', 'hover', 'responsive'],
textColor: ['focus', 'focus-within', 'hover', 'responsive'],
},
})
})
test('custom variants go to the beginning by default when sort is applied', () => {
const userConfig = {
variants: {
extend: {
backgroundColor: ['active', 'custom-variant-1', 'group-hover', 'custom-variant-2'],
},
},
}
const defaultConfig = {
prefix: '',
important: false,
separator: ':',
theme: {},
variants: {
backgroundColor: ['responsive', 'hover', 'focus'],
},
}
const result = resolveConfig([userConfig, defaultConfig])
expect(result).toMatchObject({
variants: {
backgroundColor: [
'responsive',
'custom-variant-1',
'custom-variant-2',
'group-hover',
'hover',
'focus',
'active',
],
},
})
})
test('variants can be defined as a function', () => {
const userConfig = {
variants: {
backgroundColor: ({ variants }) => [...variants('backgroundColor'), 'disabled'],
padding: ({ before }) => before(['active']),
float: ({ before }) => before(['disabled'], 'focus'),
margin: ({ before }) => before(['hover'], 'focus'),
borderWidth: ({ after }) => after(['active']),
backgroundImage: ({ after }) => after(['disabled'], 'hover'),
opacity: ({ after }) => after(['hover'], 'focus'),
rotate: ({ without }) => without(['hover']),
cursor: ({ before, after, without }) =>
without(['responsive'], before(['checked'], 'hover', after(['hover'], 'focus'))),
},
}
const otherConfig = {
variants: {
backgroundColor: ({ variants }) => [...variants('backgroundColor'), 'active'],
},
}
const defaultConfig = {
prefix: '',
important: false,
separator: ':',
theme: {},
variants: {
backgroundColor: ['responsive', 'hover', 'focus'],
padding: ['responsive', 'focus'],
float: ['responsive', 'hover', 'focus'],
margin: ['responsive'],
borderWidth: ['responsive', 'focus'],
backgroundImage: ['responsive', 'hover', 'focus'],
opacity: ['responsive'],
rotate: ['responsive', 'hover', 'focus'],
cursor: ['responsive', 'focus'],
},
}
const result = resolveConfig([userConfig, otherConfig, defaultConfig])
expect(result).toMatchObject({
prefix: '',
important: false,
separator: ':',
theme: {},
variants: {
backgroundColor: ['responsive', 'hover', 'focus', 'active', 'disabled'],
padding: ['active', 'responsive', 'focus'],
float: ['responsive', 'hover', 'disabled', 'focus'],
margin: ['responsive', 'hover'],
borderWidth: ['responsive', 'focus', 'active'],
backgroundImage: ['responsive', 'hover', 'disabled', 'focus'],
opacity: ['hover', 'responsive'],
rotate: ['responsive', 'focus'],
cursor: ['focus', 'checked', 'hover'],
},
})
})
test('core plugin configuration builds on the default list when starting with an empty object', () => {
const userConfig = {
corePlugins: { display: false },
}
const defaultConfig = {
prefix: '',
important: false,
separator: ':',
theme: {},
variants: {},
corePlugins: {},
}
const result = resolveConfig([userConfig, defaultConfig])
expect(result).toMatchObject({
prefix: '',
important: false,
separator: ':',
theme: {},
variants: {},
corePlugins: corePluginList.filter((c) => c !== 'display'),
})
})
test('core plugin configurations stack', () => {
const userConfig = {
corePlugins: { display: false },
}
const otherConfig = {
corePlugins: ({ corePlugins }) => {
return [...corePlugins, 'margin']
},
}
const defaultConfig = {
prefix: '',
important: false,
separator: ':',
theme: {},
variants: {},
corePlugins: ['float', 'display', 'padding'],
}
const result = resolveConfig([userConfig, otherConfig, defaultConfig])
expect(result).toMatchObject({
prefix: '',
important: false,
separator: ':',
theme: {},
variants: {},
corePlugins: ['float', 'padding', 'margin'],
})
})
test('plugins are merged', () => {
const userConfig = {
plugins: ['3'],
}
const otherConfig = {
plugins: ['2'],
}
const defaultConfig = {
plugins: ['1'],
prefix: '',
important: false,
separator: ':',
theme: {},
variants: {},
}
const result = resolveConfig([userConfig, otherConfig, defaultConfig])
expect(result).toMatchObject({
prefix: '',
important: false,
separator: ':',
theme: {},
variants: {},
plugins: ['1', '2', '3'],
})
})