tailwindcss/__tests__/processPlugins.test.js
2019-01-14 15:43:12 -05:00

973 lines
20 KiB
JavaScript

import _ from "lodash"
import postcss from "postcss"
import processPlugins from "../src/util/processPlugins"
function css(nodes) {
return postcss.root({ nodes }).toString()
}
function config(overrides) {
return _.defaultsDeep(overrides, {
options: {
prefix: "",
important: false,
separator: ":"
}
})
}
test("plugins can create utilities with object syntax", () => {
const { components, utilities } = processPlugins(
[
function({ addUtilities }) {
addUtilities({
".object-fill": {
"object-fit": "fill"
},
".object-contain": {
"object-fit": "contain"
},
".object-cover": {
"object-fit": "cover"
}
})
}
],
config()
)
expect(components.length).toBe(0)
expect(css(utilities)).toMatchCss(`
@variants {
.object-fill {
object-fit: fill
}
.object-contain {
object-fit: contain
}
.object-cover {
object-fit: cover
}
}
`)
})
test("plugins can create utilities with arrays of objects", () => {
const { components, utilities } = processPlugins(
[
function({ addUtilities }) {
addUtilities([
{
".object-fill": {
"object-fit": "fill"
}
},
{
".object-contain": {
"object-fit": "contain"
}
},
{
".object-cover": {
"object-fit": "cover"
}
}
])
}
],
config()
)
expect(components.length).toBe(0)
expect(css(utilities)).toMatchCss(`
@variants {
.object-fill {
object-fit: fill
}
.object-contain {
object-fit: contain
}
.object-cover {
object-fit: cover
}
}
`)
})
test("plugins can create utilities with raw PostCSS nodes", () => {
const { components, utilities } = processPlugins(
[
function({ addUtilities }) {
addUtilities([
postcss.rule({ selector: ".object-fill" }).append([
postcss.decl({
prop: "object-fit",
value: "fill"
})
]),
postcss.rule({ selector: ".object-contain" }).append([
postcss.decl({
prop: "object-fit",
value: "contain"
})
]),
postcss.rule({ selector: ".object-cover" }).append([
postcss.decl({
prop: "object-fit",
value: "cover"
})
])
])
}
],
config()
)
expect(components.length).toBe(0)
expect(css(utilities)).toMatchCss(`
@variants {
.object-fill {
object-fit: fill
}
.object-contain {
object-fit: contain
}
.object-cover {
object-fit: cover
}
}
`)
})
test("plugins can create utilities with mixed object styles and PostCSS nodes", () => {
const { components, utilities } = processPlugins(
[
function({ addUtilities }) {
addUtilities([
{
".object-fill": {
objectFit: "fill"
}
},
postcss.rule({ selector: ".object-contain" }).append([
postcss.decl({
prop: "object-fit",
value: "contain"
})
]),
postcss.rule({ selector: ".object-cover" }).append([
postcss.decl({
prop: "object-fit",
value: "cover"
})
])
])
}
],
config()
)
expect(components.length).toBe(0)
expect(css(utilities)).toMatchCss(`
@variants {
.object-fill {
object-fit: fill
}
.object-contain {
object-fit: contain
}
.object-cover {
object-fit: cover
}
}
`)
})
test("plugins can create utilities with variants", () => {
const { components, utilities } = processPlugins(
[
function({ addUtilities }) {
addUtilities(
{
".object-fill": {
"object-fit": "fill"
},
".object-contain": {
"object-fit": "contain"
},
".object-cover": {
"object-fit": "cover"
}
},
["responsive", "hover", "group-hover", "focus"]
)
}
],
config()
)
expect(components.length).toBe(0)
expect(css(utilities)).toMatchCss(`
@variants responsive, hover, group-hover, focus {
.object-fill {
object-fit: fill
}
.object-contain {
object-fit: contain
}
.object-cover {
object-fit: cover
}
}
`)
})
test("plugins can create components with object syntax", () => {
const { components, utilities } = processPlugins(
[
function({ addComponents }) {
addComponents({
".btn-blue": {
backgroundColor: "blue",
color: "white",
padding: ".5rem 1rem",
borderRadius: ".25rem"
},
".btn-blue:hover": {
backgroundColor: "darkblue"
}
})
}
],
config()
)
expect(utilities.length).toBe(0)
expect(css(components)).toMatchCss(`
.btn-blue {
background-color: blue;
color: white;
padding: .5rem 1rem;
border-radius: .25rem
}
.btn-blue:hover {
background-color: darkblue
}
`)
})
test("plugins can create components with raw PostCSS nodes", () => {
const { components, utilities } = processPlugins(
[
function({ addComponents }) {
addComponents([
postcss.rule({ selector: ".btn-blue" }).append([
postcss.decl({
prop: "background-color",
value: "blue"
}),
postcss.decl({
prop: "color",
value: "white"
}),
postcss.decl({
prop: "padding",
value: ".5rem 1rem"
}),
postcss.decl({
prop: "border-radius",
value: ".25rem"
})
]),
postcss.rule({ selector: ".btn-blue:hover" }).append([
postcss.decl({
prop: "background-color",
value: "darkblue"
})
])
])
}
],
config()
)
expect(utilities.length).toBe(0)
expect(css(components)).toMatchCss(`
.btn-blue {
background-color: blue;
color: white;
padding: .5rem 1rem;
border-radius: .25rem
}
.btn-blue:hover {
background-color: darkblue
}
`)
})
test("plugins can create components with mixed object styles and raw PostCSS nodes", () => {
const { components, utilities } = processPlugins(
[
function({ addComponents }) {
addComponents([
postcss.rule({ selector: ".btn-blue" }).append([
postcss.decl({
prop: "background-color",
value: "blue"
}),
postcss.decl({
prop: "color",
value: "white"
}),
postcss.decl({
prop: "padding",
value: ".5rem 1rem"
}),
postcss.decl({
prop: "border-radius",
value: ".25rem"
})
]),
{
".btn-blue:hover": {
backgroundColor: "darkblue"
}
}
])
}
],
config()
)
expect(utilities.length).toBe(0)
expect(css(components)).toMatchCss(`
.btn-blue {
background-color: blue;
color: white;
padding: .5rem 1rem;
border-radius: .25rem
}
.btn-blue:hover {
background-color: darkblue
}
`)
})
test("plugins can create components with media queries with object syntax", () => {
const { components, utilities } = processPlugins(
[
function({ addComponents }) {
addComponents({
".container": {
width: "100%"
},
"@media (min-width: 100px)": {
".container": {
maxWidth: "100px"
}
},
"@media (min-width: 200px)": {
".container": {
maxWidth: "200px"
}
},
"@media (min-width: 300px)": {
".container": {
maxWidth: "300px"
}
}
})
}
],
config()
)
expect(utilities.length).toBe(0)
expect(css(components)).toMatchCss(`
.container {
width: 100%
}
@media (min-width: 100px) {
.container {
max-width: 100px
}
}
@media (min-width: 200px) {
.container {
max-width: 200px
}
}
@media (min-width: 300px) {
.container {
max-width: 300px
}
}
`)
})
test("media queries can be defined multiple times using objects-in-array syntax", () => {
const { components, utilities } = processPlugins(
[
function({ addComponents }) {
addComponents([
{
".container": {
width: "100%"
},
"@media (min-width: 100px)": {
".container": {
maxWidth: "100px"
}
}
},
{
".btn": {
padding: "1rem .5rem",
display: "block"
},
"@media (min-width: 100px)": {
".btn": {
display: "inline-block"
}
}
}
])
}
],
config()
)
expect(utilities.length).toBe(0)
expect(css(components)).toMatchCss(`
.container {
width: 100%
}
@media (min-width: 100px) {
.container {
max-width: 100px
}
}
.btn {
padding: 1rem .5rem;
display: block
}
@media (min-width: 100px) {
.btn {
display: inline-block
}
}
`)
})
test("plugins can create nested rules", () => {
const { components, utilities } = processPlugins(
[
function({ addComponents }) {
addComponents({
".btn-blue": {
backgroundColor: "blue",
color: "white",
padding: ".5rem 1rem",
borderRadius: ".25rem",
"&:hover": {
backgroundColor: "darkblue"
},
"@media (min-width: 500px)": {
"&:hover": {
backgroundColor: "orange"
}
},
"> a": {
color: "red"
},
"h1 &": {
color: "purple"
}
}
})
}
],
config()
)
expect(utilities.length).toBe(0)
expect(css(components)).toMatchCss(`
.btn-blue {
background-color: blue;
color: white;
padding: .5rem 1rem;
border-radius: .25rem;
}
.btn-blue:hover {
background-color: darkblue;
}
@media (min-width: 500px) {
.btn-blue:hover {
background-color: orange;
}
}
.btn-blue > a {
color: red;
}
h1 .btn-blue {
color: purple;
}
`)
})
test("plugins can create rules with escaped selectors", () => {
const { components, utilities } = processPlugins(
[
function({ e, addUtilities }) {
addUtilities({
[`.${e("top-1/4")}`]: {
top: "25%"
}
})
}
],
config()
)
expect(components.length).toBe(0)
expect(css(utilities)).toMatchCss(`
@variants {
.top-1\\/4 {
top: 25%
}
}
`)
})
test("plugins can access the current config", () => {
const { components, utilities } = processPlugins(
[
function({ addComponents, config }) {
const containerClasses = [
{
".container": {
width: "100%"
}
}
]
_.forEach(config("screens"), breakpoint => {
containerClasses.push({
[`@media (min-width: ${breakpoint})`]: {
".container": { maxWidth: breakpoint }
}
})
})
addComponents(containerClasses)
}
],
config({
screens: {
sm: "576px",
md: "768px",
lg: "992px",
xl: "1200px"
}
})
)
expect(utilities.length).toBe(0)
expect(css(components)).toMatchCss(`
.container {
width: 100%
}
@media (min-width: 576px) {
.container {
max-width: 576px
}
}
@media (min-width: 768px) {
.container {
max-width: 768px
}
}
@media (min-width: 992px) {
.container {
max-width: 992px
}
}
@media (min-width: 1200px) {
.container {
max-width: 1200px
}
}
`)
})
test("plugins can provide fallbacks to keys missing from the config", () => {
const { components, utilities } = processPlugins(
[
function({ addComponents, config }) {
addComponents({
".btn": {
borderRadius: config("borderRadius.default", ".25rem")
}
})
}
],
config({
borderRadius: {
"1": "1px",
"2": "2px",
"4": "4px",
"8": "8px"
}
})
)
expect(utilities.length).toBe(0)
expect(css(components)).toMatchCss(`
.btn {
border-radius: .25rem
}
`)
})
test("variants are optional when adding utilities", () => {
const { utilities } = processPlugins(
[
function({ addUtilities }) {
addUtilities({
".border-collapse": {
"border-collapse": "collapse"
}
})
}
],
config()
)
expect(css(utilities)).toMatchCss(`
@variants {
.border-collapse {
border-collapse: collapse
}
}`)
})
test("plugins can add multiple sets of utilities and components", () => {
const { components, utilities } = processPlugins(
[
function({ addUtilities, addComponents }) {
addComponents({
".card": {
padding: "1rem",
borderRadius: ".25rem"
}
})
addUtilities({
".skew-12deg": {
transform: "skewY(-12deg)"
}
})
addComponents({
".btn": {
padding: "1rem .5rem",
display: "inline-block"
}
})
addUtilities({
".border-collapse": {
borderCollapse: "collapse"
}
})
}
],
config()
)
expect(css(utilities)).toMatchCss(`
@variants {
.skew-12deg {
transform: skewY(-12deg)
}
}
@variants {
.border-collapse {
border-collapse: collapse
}
}
`)
expect(css(components)).toMatchCss(`
.card {
padding: 1rem;
border-radius: .25rem
}
.btn {
padding: 1rem .5rem;
display: inline-block
}
`)
})
test("plugins respect prefix and important options by default when adding utilities", () => {
const { utilities } = processPlugins(
[
function({ addUtilities }) {
addUtilities({
".rotate-90": {
transform: "rotate(90deg)"
}
})
}
],
config({
options: {
prefix: "tw-",
important: true
}
})
)
expect(css(utilities)).toMatchCss(`
@variants {
.tw-rotate-90 {
transform: rotate(90deg) !important
}
}
`)
})
test("component declarations respect the 'prefix' option by default", () => {
const { components } = processPlugins(
[
function({ addComponents }) {
addComponents({
".btn-blue": {
backgroundColor: "blue"
}
})
}
],
config({
options: {
prefix: "tw-"
}
})
)
expect(css(components)).toMatchCss(`
.tw-btn-blue {
background-color: blue
}
`)
})
test("component declarations can optionally ignore 'prefix' option", () => {
const { components } = processPlugins(
[
function({ addComponents }) {
addComponents(
{
".btn-blue": {
backgroundColor: "blue"
}
},
{ respectPrefix: false }
)
}
],
config({
options: {
prefix: "tw-"
}
})
)
expect(css(components)).toMatchCss(`
.btn-blue {
background-color: blue
}
`)
})
test("component declarations are not affected by the 'important' option", () => {
const { components } = processPlugins(
[
function({ addComponents }) {
addComponents({
".btn-blue": {
backgroundColor: "blue"
}
})
}
],
config({
options: {
important: true
}
})
)
expect(css(components)).toMatchCss(`
.btn-blue {
background-color: blue
}
`)
})
test("plugins can apply the user's chosen prefix to components manually", () => {
const { components } = processPlugins(
[
function({ addComponents, prefix }) {
addComponents(
{
[prefix(".btn-blue")]: {
backgroundColor: "blue"
}
},
{ respectPrefix: false }
)
}
],
config({
options: {
prefix: "tw-"
}
})
)
expect(css(components)).toMatchCss(`
.tw-btn-blue {
background-color: blue
}
`)
})
test("prefix can optionally be ignored for utilities", () => {
const { utilities } = processPlugins(
[
function({ addUtilities }) {
addUtilities(
{
".rotate-90": {
transform: "rotate(90deg)"
}
},
{
respectPrefix: false
}
)
}
],
config({
options: {
prefix: "tw-",
important: true
}
})
)
expect(css(utilities)).toMatchCss(`
@variants {
.rotate-90 {
transform: rotate(90deg) !important
}
}
`)
})
test("important can optionally be ignored for utilities", () => {
const { utilities } = processPlugins(
[
function({ addUtilities }) {
addUtilities(
{
".rotate-90": {
transform: "rotate(90deg)"
}
},
{
respectImportant: false
}
)
}
],
config({
options: {
prefix: "tw-",
important: true
}
})
)
expect(css(utilities)).toMatchCss(`
@variants {
.tw-rotate-90 {
transform: rotate(90deg)
}
}
`)
})
test("variants can still be specified when ignoring prefix and important options", () => {
const { utilities } = processPlugins(
[
function({ addUtilities }) {
addUtilities(
{
".rotate-90": {
transform: "rotate(90deg)"
}
},
{
variants: ["responsive", "hover", "focus"],
respectImportant: false,
respectPrefix: false
}
)
}
],
config({
options: {
prefix: "tw-",
important: true
}
})
)
expect(css(utilities)).toMatchCss(`
@variants responsive, hover, focus {
.rotate-90 {
transform: rotate(90deg)
}
}
`)
})
test("prefix will prefix all classes in a selector", () => {
const { components } = processPlugins(
[
function({ addComponents, prefix }) {
addComponents(
{
[prefix(".btn-blue .w-1\\/4 > h1.text-xl + a .bar")]: {
backgroundColor: "blue"
}
},
{ respectPrefix: false }
)
}
],
config({
options: {
prefix: "tw-"
}
})
)
expect(css(components)).toMatchCss(`
.tw-btn-blue .tw-w-1\\/4 > h1.tw-text-xl + a .tw-bar {
background-color: blue
}
`)
})